1
2
3
4 package org.xwt.mips;
5 import java.io.*;
6
7 public abstract class Runtime implements Syscalls, Errno,Registers {
8
9 public final static int PAGE_SIZE = 4096;
10 public final static int PAGE_WORDS = (int)(PAGE_SIZE >>> 2);
11 public final static int PAGE_SHIFT = 12;
12
13 protected final static int TOTAL_PAGES = 65536;
14
15 protected final static int STACK_PAGES = 256;
16
17 protected final static int BRK_LIMIT = TOTAL_PAGES - STACK_PAGES - 1024;
18
19
32
33
35 protected final static int STUFF_BASE = (TOTAL_PAGES-3)*PAGE_SIZE;
36 protected final static int ARGS_ADDR = STUFF_BASE + PAGE_SIZE;
37 protected final static int USER_INFO_ADDR = STUFF_BASE;
38
39
40 protected final static int INITIAL_SP = STUFF_BASE - PAGE_SIZE;
41
42
46 private final boolean allowEmptyPages;
47
48 private final static int[] _emptyPage = new int[0];
49
50
51 private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
52
53
54 protected final int[][] readPages;
55
57 protected final int[][] writePages;
58
59
61 protected int brk;
62
63
64 protected int entryPoint;
65
66
67 public final static int UNINITIALIZED = 0;
68
69 public final static int INITIALIZED = 1;
70
71 public final static int RUNNING = 2;
72
73 public final static int PAUSED = 3;
74
75 public final static int DONE = 4;
76
77
78 protected int state = UNINITIALIZED;
79
80 public final int getState() { return state; }
81
82
84 protected int exitStatus;
85
86
87 private final static int OPEN_MAX = 256;
88
89 private FileDescriptor[] fds;
90
91
92 private byte[] _byteBuf = null;
93
95 private final static int MAX_CHUNK = 4*1024*1024;
96
97
98 public static final int PID = 1;
99
100
103 protected abstract void _execute() throws ExecutionException;
104
105
112 protected abstract void _start(int pc);
113
114
116 public Runtime() { this(false); }
117
118
119 public Runtime(boolean allowEmptyPages) {
120 this.allowEmptyPages = allowEmptyPages;
121 readPages = new int[TOTAL_PAGES][];
122 writePages = new int[TOTAL_PAGES][];
123 for(int i=0;i<STACK_PAGES;i++)
124 readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
125 }
126
127
129 protected final void initPages(int[] src, int addr, boolean ro) {
130 for(int i=0;i<src.length;) {
131 int page = addr >>> PAGE_SHIFT;
132 int start = (addr&(PAGE_SIZE-1))>>2;
133 int elements = min(PAGE_WORDS-start,src.length-i);
134 if(readPages[page]==null) {
135 initPage(page,ro);
136 } else if(!ro) {
137 if(writePages[page] == null) writePages[page] = readPages[page];
138 }
139 System.arraycopy(src,i,readPages[page],start,elements);
140 i += elements;
141 addr += elements*4;
142 }
143 }
144
145
146 protected final void clearPages(int addr, int words) {
147 for(int i=0;i<words;) {
148 int page = addr >>> PAGE_SHIFT;
149 int start = (addr&(PAGE_SIZE-1))>>2;
150 int elements = min(PAGE_WORDS-start,words-i);
151 if(readPages[page]==null) {
152 readPages[page] = writePages[page] = emptyPage();
153 } else {
154 if(writePages[page] == null) writePages[page] = readPages[page];
155 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
156 }
157 i += elements;
158 addr += elements*4;
159 }
160 }
161
162
164 public final void copyin(int addr, byte[] a, int length) throws ReadFaultException {
165 int n=0;
166 if((addr&3)!=0) {
167 int word = memRead(addr&~3);
168 switch(addr&3) {
169 case 1: a[n++] = (byte)((word>>>16)&0xff); if(length-n==0) break;
170 case 2: a[n++] = (byte)((word>>> 8)&0xff); if(length-n==0) break;
171 case 3: a[n++] = (byte)((word>>> 0)&0xff); if(length-n==0) break;
172 }
173 addr = (addr&~3)+4;
174 }
175 while(length-n > 3) {
176 int start = (addr&(PAGE_SIZE-1))>>2;
177 int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
178 int[] page = readPages[addr >>> PAGE_SHIFT];
179 if(page == null) throw new ReadFaultException(addr);
180 if(page == _emptyPage) { addr+=(end-start)<<2; n+=(end-start)<<2; continue; }
181 for(int i=start;i<end;i++,addr+=4) {
182 int word = page[i];
183 a[n++] = (byte)((word>>>24)&0xff); a[n++] = (byte)((word>>>16)&0xff);
184 a[n++] = (byte)((word>>> 8)&0xff); a[n++] = (byte)((word>>> 0)&0xff);
185 }
186 }
187 if(length-n > 0) {
188 int word = memRead(addr);
189 if(length-n >= 1) a[n+0] = (byte)((word>>>24)&0xff);
190 if(length-n >= 2) a[n+1] = (byte)((word>>>16)&0xff);
191 if(length-n >= 3) a[n+2] = (byte)((word>>> 8)&0xff);
192 }
193 }
194
195
197 public final void copyout(byte[] a, int addr, int length) throws FaultException {
198 int n=0;
199 if((addr&3)!=0) {
200 int word = memRead(addr&~3);
201 switch(addr&3) {
202 case 1: word = (word&0xff00ffff)|((a[n]&0xff)<<16); n++; if(length-n==0) break;
203 case 2: word = (word&0xffff00ff)|((a[n]&0xff)<< 8); n++; if(length-n==0) break;
204 case 3: word = (word&0xffffff00)|((a[n]&0xff)<< 0); n++; if(length-n==0) break;
205 }
206 memWrite(addr&~3,word);
207 addr = (addr&~3)+4;
208 }
209
210 while(length-n > 3) {
211 int start = (addr&(PAGE_SIZE-1))>>2;
212 int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
213 int[] page = writePages[addr >>> PAGE_SHIFT];
214 if(page == null) throw new WriteFaultException(addr);
215 if(page == _emptyPage) { memWrite(addr,0); page = writePages[addr >>> PAGE_SHIFT]; }
216 for(int i=start;i<end;i++,addr+=4) {
217 int word = ((a[n+0]&0xff)<<24)|((a[n+1]&0xff)<<16)|((a[n+2]&0xff)<<8)|((a[n+3]&0xff)<<0); n+=4;
218 page[i] = word;
219 }
220 }
221 if(length-n > 0) {
222 int word = memRead(addr);
223 if(length-n >= 1) { word = (word&0x00ffffff)|((a[n+0]&0xff)<<24); }
224 if(length-n >= 2) { word = (word&0xff00ffff)|((a[n+1]&0xff)<<16); }
225 if(length-n >= 3) { word = (word&0xffff00ff)|((a[n+2]&0xff)<< 8); }
226 memWrite(addr,word);
227 }
228 }
229
230
231 public final int memRead(int addr) throws ReadFaultException {
232 if((addr & 3) != 0) throw new ReadFaultException(addr);
233 int page = addr >>> PAGE_SHIFT;
234 int entry = (addr >>> 2) & (PAGE_WORDS-1);
235 try {
236 return readPages[page][entry];
237 } catch(ArrayIndexOutOfBoundsException e) {
238 if(page < 0) throw e;
239 if(page > readPages.length) throw new ReadFaultException(addr);
240 if(readPages[page] != _emptyPage) throw e;
241 initPage(page);
242 return 0;
243 } catch(NullPointerException e) {
244 throw new ReadFaultException(addr);
245 }
246 }
247
248
249 public final void memWrite(int addr, int value) throws WriteFaultException {
250 if((addr & 3) != 0) throw new WriteFaultException(addr);
251 int page = addr >>> PAGE_SHIFT;
252 int entry = (addr>>>2)&(PAGE_WORDS-1);
253 try {
254 writePages[page][entry] = value;
255 } catch(ArrayIndexOutOfBoundsException e) {
256 if(page < 0) throw e;
257 if(page > writePages.length) throw new WriteFaultException(addr);
258 if(readPages[page] != _emptyPage) throw e;
259 initPage(page);
260 writePages[page][entry] = value;
261 } catch(NullPointerException e) {
262 throw new WriteFaultException(addr);
263 }
264 }
265
266
267 private final void initPage(int page) { initPage(page,false); }
268
269 private final void initPage(int page, boolean ro) {
270 int[] buf = new int[PAGE_WORDS];
271 writePages[page] = ro ? null : buf;
272 readPages[page] = buf;
273 }
274
275
277 public final int exitStatus() {
278 if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
279 return exitStatus;
280 }
281
282
284 public final int run(String[] args) throws ExecutionException {
285 start(args);
286 for(;;) {
287 if(execute()) break;
288 System.err.println("WARNING: Pause requested while executing run()");
289 try { Thread.sleep(500); } catch(InterruptedException e) { }
290 }
291 return exitStatus();
292 }
293
294
295 private void addArgs(String[] args) throws ExecutionException {
296 int count = args.length;
297 byte[] nullTerminator = new byte[1];
298 int total = 4;
299 for(int i=0;i<count;i++) total += args[i].length() + 1 + 4;
300 if(total >= PAGE_SIZE-4) throw new ExecutionException("Arguments too large");
301 int start = ARGS_ADDR;
302 int addr = start + (count+1)*4;
303 int[] table = new int[count+1];
304 for(int i=0;i<count;i++) {
305 byte[] a;
306 try { a = args[i].getBytes("US-ASCII"); } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
307 table[i] = addr;
308 copyout(a,addr,a.length);
309 addr += a.length;
310 copyout(nullTerminator,addr,1);
311 addr += 1;
312 }
313 addr=start;
314 for(int i=0;i<count;i++) {
315 memWrite(addr,table[i]);
316 addr += 4;
317 }
318 }
319
320
324 public void setUserInfo(int index, int word) {
325 if(index < 0 || index >= 1024) throw new IllegalStateException("setUserInfo called with index >= 1024");
326 try {
327 memWrite(USER_INFO_ADDR+index*4,word);
328 } catch(FaultException e) { throw new Error("should never happen: " + e); }
329 }
330
331
333 public int getUserInfo(int index) {
334 if(index < 0 || index >= 1024) throw new IllegalStateException("setUserInfo called with index >= 1024");
335 try {
336 return memRead(USER_INFO_ADDR+index*4);
337 } catch(FaultException e) { throw new Error("should never happen: " + e); }
338 }
339
340
341 public final boolean execute() throws ExecutionException {
342 if(state == PAUSED) state = RUNNING;
343 if(state != RUNNING) throw new IllegalStateException("execute() called in inappropriate state");
344 _execute();
345 if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state");
346 return state == DONE;
347 }
348
349
350 public final void start(String[] args) throws ExecutionException {
351 if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
352 _start(entryPoint);
353 addArgs(args);
354 fds = new FileDescriptor[OPEN_MAX];
355 fds[0] = new InputStreamFD(System.in) { public boolean isatty() { return true; } };
356 fds[1] = new OutputStreamFD(System.out) { public boolean isatty() { return true; } };
357 fds[2] = new OutputStreamFD(System.err) { public boolean isatty() { return true; } };
358 state = PAUSED;
359 }
360
361
363 protected boolean allowFileAccess(String fileName, boolean write) {
364 System.err.println("Allowing " + (write?"write":"read-only") + " to " + fileName);
365 return true;
366 }
367
368
371 protected int allocFDEnt(FileDescriptor fd) {
372 int i;
373 for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
374 if(i==OPEN_MAX) return -1;
375 fds[i] = fd;
376 return i;
377 }
378
379
380 private int sys_open(int addr, int flags, int mode) {
381 final int O_RDONLY = 0;
382 final int O_WRONLY = 1;
383 final int O_RDWR = 2;
384 final int O_APPEND = 0x0008;
385 final int O_CREAT = 0x0200;
386 final int O_NONBLOCK = 0x4000;
387 final int O_EXCL = 0x0800;
388
389 if((flags & O_APPEND) != 0) {
390 System.err.println("WARNING: O_APPEND not supported");
391 return -EOPNOTSUPP;
392 }
393 if((flags & O_NONBLOCK) != 0) {
394 System.err.println("WARNING: O_NONBLOCK not supported");
395 return -EOPNOTSUPP;
396 }
397
398 try {
399 String fileName = cstring(addr);
400 if(!allowFileAccess(fileName,(flags&3)!=0)) return -EACCES;
401
402 File f = new File(fileName);
403 if((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)
404 if(!f.createNewFile()) return -EEXIST;
405 if(f.exists()) {
406 if(f.length() >= Integer.MAX_VALUE) return -EOPNOTSUPP;
407 } else {
408 if((flags & O_CREAT) == 0) return -ENOENT;
409 }
410 int fdn = allocFDEnt(new RegularFileDescriptor(f,flags&3));
411 return fdn == -1 ? -ENFILE : fdn;
412 } catch(FaultException e) {
413 return -EFAULT;
414 } catch(FileNotFoundException e) {
415 if(e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
416 return -ENOENT;
417 } catch(IOException e) {
418 return -EIO;
419 }
420 }
421
422
423 private int sys_write(int fdn, int addr, int count) {
424 int n = 0;
425 int r;
426 FileDescriptor fd;
427 count = Math.min(count,MAX_CHUNK);
428 try {
429 fd = fds[fdn];
430 if(fd == null || !fd.writable()) return -EBADFD;
431 } catch(ArrayIndexOutOfBoundsException e) {
432 return -EBADFD;
433 }
434 try {
435 byte[] buf = byteBuf(count);
436 copyin(addr,buf,count);
437 return fd.write(buf,0,count);
438 } catch(FaultException e) {
439 System.err.println(e);
440 return -EFAULT;
441 } catch(IOException e) {
442 System.err.println(e);
443 return -EIO;
444 }
445 }
446
447
448 private int sys_read(int fdn, int addr, int count) {
449 FileDescriptor fd;
450 count = Math.min(count,MAX_CHUNK);
451 try {
452 fd = fds[fdn];
453 if(fd == null || !fd.readable()) return -EBADFD;
454 } catch(ArrayIndexOutOfBoundsException e) {
455 return -EBADFD;
456 }
457 try {
458 byte[] buf = byteBuf(count);
459 int n = fd.read(buf,0,count);
460 copyout(buf,addr,n);
461 return n;
462 } catch(FaultException e) {
463 System.err.println(e);
464 return -EFAULT;
465 } catch(IOException e) {
466 System.err.println(e);
467 return -EIO;
468 }
469 }
470
471
472 private int sys_close(int fdn) {
473 FileDescriptor fd;
474 try {
475 fd = fds[fdn];
476 if(fd == null) return -EBADFD;
477 } catch(ArrayIndexOutOfBoundsException e) {
478 return -EBADFD;
479 }
480 fds[fdn] = null;
481 fd.close();
482 return 0;
483 }
484
485
486 private int sys_seek(int fdn, int offset, int whence) {
487 FileDescriptor fd;
488 try {
489 fd = fds[fdn];
490 if(fd == null || !fd.readable()) return -EBADFD;
491 } catch(ArrayIndexOutOfBoundsException e) {
492 return -EBADFD;
493 }
494 if(whence != FileDescriptor.SEEK_SET && whence != FileDescriptor.SEEK_CUR && whence != FileDescriptor.SEEK_END) return -EINVAL;
495 try {
496 int n = fd.seek(offset,whence);
497 return n < 0 ? -ESPIPE : n;
498 } catch(IOException e) {
499 return -ESPIPE;
500 }
501 }
502
503
504 private int stat(FileInfo fi, int addr) {
505 int size = fi.size();
506 try {
507 memWrite(addr+0,(1<<16)|1);
508 memWrite(addr+4,(fi.type() & 0xf000)|0644);
509 memWrite(addr+8,1<<16);
510 memWrite(addr+12,0);
511 memWrite(addr+16,size);
512 memWrite(addr+20,0);
513
514 memWrite(addr+28,(int)(fi.modTime()/1000));
515
516 memWrite(addr+36,0);
517
518 memWrite(addr+44,512);
519 memWrite(addr+48,(size+511)&(~511));
520
521
522 } catch(FaultException e) {
523 System.err.println(e);
524 return -EFAULT;
525 }
526 return 0;
527 }
528
529
530 private int sys_fstat(int fdn, int addr) {
531 FileDescriptor fd;
532 try {
533 fd = fds[fdn];
534 if(fd == null) return -EBADFD;
535 } catch(ArrayIndexOutOfBoundsException e) {
536 return -EBADFD;
537 }
538 return stat(fd.fileInfo(),addr);
539 }
540
541
543 public int sbrk(int incr) {
544 if(incr==0) return brk<<PAGE_SHIFT;
545 int oldBrk = brk;
546 int newBrk = oldBrk + ((incr+PAGE_SIZE-1)>>PAGE_SHIFT);
547 if(newBrk >= BRK_LIMIT) {
548 System.err.println("Hit BRK_LIMIT");
549 return -ENOMEM;
550 }
551 for(int i=oldBrk;i<newBrk;i++)
552 readPages[i] = writePages[i] = emptyPage();
553 brk = newBrk;
554 return oldBrk<<PAGE_SHIFT;
555 }
556
557
561 private int sys_kill(int pid, int signal) {
562
563
564 if(pid != PID) return -ESRCH;
565 if(signal < 0 || signal >= 32) return -EINVAL;
566 switch(signal) {
567 case 0: return 0;
568 case 17:
569 case 18:
570 case 21:
571 case 22:
572 state = PAUSED;
573 break;
574 case 19:
575 case 20:
576 case 23:
577 case 28:
578 break;
579 default: {
580 String msg = "Terminating on signal: " + signal + "\n";
581 exitStatus = 1;
582 state = DONE;
583 if(fds[2]==null) {
584 System.out.print(msg);
585 } else {
586 try {
587 byte[] b = msg.getBytes("US-ASCII");
588 fds[2].write(b,0,b.length);
589 }
590 catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
591 catch(IOException e) { }
592 }
593 }
594 }
595 return 0;
596 }
597
598
599 private int sys_getpid() { return PID; }
600
601
606 protected int syscall(int syscall, int a, int b, int c, int d) {
607 switch(syscall) {
608 case SYS_null: return 0;
609 case SYS_exit: exitStatus = a; state = DONE; return 0;
610 case SYS_pause: state = PAUSED; return 0;
611 case SYS_write: return sys_write(a,b,c);
612 case SYS_fstat: return sys_fstat(a,b);
613 case SYS_sbrk: return sbrk(a);
614 case SYS_open: return sys_open(a,b,c);
615 case SYS_close: return sys_close(a);
616 case SYS_read: return sys_read(a,b,c);
617 case SYS_seek: return sys_seek(a,b,c);
618 case SYS_kill: return sys_kill(a,b);
619 case SYS_getpid: return sys_getpid();
620 default:
621 System.err.println("Attempted to use unknown syscall: " + syscall);
622 return -ENOSYS;
623 }
624 }
625
626
627 public String cstring(int addr) throws ReadFaultException {
628 StringBuffer sb = new StringBuffer();
629 for(;;) {
630 int word = memRead(addr&~3);
631 switch(addr&3) {
632 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
633 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
634 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
635 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
636 }
637 }
638 }
639
640
641 public static class FileInfo {
642 public static final int S_IFIFO = 0010000;
643 public static final int S_FCHR = 0020000;
644 public static final int S_IFDIR = 0040000;
645 public static final int S_IFREG = 0100000;
646
647 public int size() { return 0; }
648 public int type() { return S_IFIFO; }
649 public long modTime() { return 0; }
650 }
651
652
653 public static class FileFileInfo extends FileInfo {
654 public File f;
655 public FileFileInfo(File f) { this.f = f; }
656 public int size() { return (int)f.length(); }
657 public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
658 public long modTime() { return f.lastModified(); }
659 }
660
661
662 public static abstract class FileDescriptor {
663 protected final static int SEEK_SET = 0;
664 protected final static int SEEK_CUR = 1;
665 protected final static int SEEK_END = 2;
666
667
668 public boolean readable() { return false; }
669
670 public boolean writable() { return false; }
671
672 private static final FileInfo nullFi = new FileInfo();
673 private FileInfo fi;
674 public FileInfo fileInfo() { return fi; }
675
676
677 FileDescriptor() { this(null); }
678
679 FileDescriptor(FileInfo fi) { this.fi = fi==null ? nullFi : fi; }
680
681
682 public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
683
684 public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
685
686
687 public int seek(int n, int whence) throws IOException { return -1; }
688
689
690 public boolean isatty() { return false; }
691
692
693 void close() { }
694 }
695
696
697 public static class RegularFileDescriptor extends FileDescriptor {
698 private int mode;
699 private RandomAccessFile raf;
700 public boolean readable() { return mode != 1; }
701 public boolean writable() { return mode != 0; }
702
703 RegularFileDescriptor(File f,int m) throws IOException {
704 super(new FileFileInfo(f));
705 String mode = m == 0 ? "r" : "rw";
706 this.mode = m;
707 raf = new RandomAccessFile(f,mode);
708 if(raf.length() >= Integer.MAX_VALUE) throw new IOException("File too large");
709 }
710
711 public int seek(int n_, int whence) throws IOException {
712 long n = n_;
713 switch(whence) {
714 case SEEK_SET: break;
715 case SEEK_CUR: n += raf.getFilePointer(); break;
716 case SEEK_END: n += raf.length(); break;
717 default: return -1;
718 }
719 raf.seek(n);
720 return (int)n;
721 }
722
723 public int write(byte[] a, int off, int length) throws IOException { raf.write(a,off,length); return length; }
724 public int read(byte[] a, int off, int length) throws IOException { int n = raf.read(a,off,length); return n < 0 ? 0 : n; }
725
726 void close() { try { raf.close(); } catch(Exception e) { } }
727 }
728
729 public class OutputStreamFD extends FileDescriptor {
730 private OutputStream os;
731 public boolean writable() { return true; }
732 public OutputStreamFD(OutputStream os) { this.os = os; }
733 public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
734 }
735
736 public class InputStreamFD extends FileDescriptor {
737 private InputStream is;
738 public boolean readable() { return true; }
739 public InputStreamFD(InputStream is) { this.is = is; }
740 public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
741 }
742
743
744 public static class ReadFaultException extends FaultException {
745 public ReadFaultException(int addr) { super(addr); }
746 }
747 public static class WriteFaultException extends FaultException {
748 public WriteFaultException(int addr) { super(addr); }
749 }
750 public static abstract class FaultException extends ExecutionException {
751 private int addr;
752 public FaultException(int addr) { this.addr = addr; }
753 public String getMessage() { return "fault at: " + toHex(addr); }
754 }
755 public static class ExecutionException extends Exception {
756 public ExecutionException() { }
757 public ExecutionException(String s) { super(s); }
758 }
759
760
761 private byte[] byteBuf(int size) {
762 if(_byteBuf==null) _byteBuf = new byte[size];
763 else if(_byteBuf.length < size)
764 _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
765 return _byteBuf;
766 }
767
768 protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
769 protected final static int min(int a, int b) { return a < b ? a : b; }
770 protected final static int max(int a, int b) { return a > b ? a : b; }
771 }
772