diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/bufio.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/bufio.b')
| -rw-r--r-- | appl/lib/bufio.b | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/appl/lib/bufio.b b/appl/lib/bufio.b new file mode 100644 index 00000000..5960e310 --- /dev/null +++ b/appl/lib/bufio.b @@ -0,0 +1,533 @@ +implement Bufio; + +include "sys.m"; + sys: Sys; + +include "bufio.m"; + +UTFself: con 16r80; # ascii and UTF sequences are the same (<) +Maxrune: con 8; # could probably be Sys->UTFmax +Bufsize: con Sys->ATOMICIO; + +Filler: adt +{ + iobuf: ref Iobuf; + fill: BufioFill; + next: cyclic ref Filler; +}; + +fillers: ref Filler; + +create(filename: string, mode, perm: int): ref Iobuf +{ + if (sys == nil) + sys = load Sys Sys->PATH; + if ((fd := sys->create(filename, mode, perm)) == nil) + return nil; + return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, big 0, big 0, mode, mode); +} + +open(filename: string, mode: int): ref Iobuf +{ + if (sys == nil) + sys = load Sys Sys->PATH; + if ((fd := sys->open(filename, mode)) == nil) + return nil; + return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, big 0, big 0, mode, mode); +} + +fopen(fd: ref Sys->FD, mode: int): ref Iobuf +{ + if (sys == nil) + sys = load Sys Sys->PATH; + if ((filpos := sys->seek(fd, big 0, 1)) < big 0) + filpos = big 0; + return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, filpos, filpos, mode, mode); +} + +sopen(input: string): ref Iobuf +{ + return aopen(array of byte input); +} + +aopen(b: array of byte): ref Iobuf +{ + if (sys == nil) + sys = load Sys Sys->PATH; + return ref Iobuf(nil, b, 0, len b, 0, big 0, big 0, OREAD, OREAD); +} + +readchunk(b: ref Iobuf): int +{ + if (b.fd == nil){ + if ((f := filler(b)) != nil){ + if ((n := f.fill->fill(b)) == EOF) + nofill(b); + return n; + } + return EOF; + } + if (b.filpos != b.bufpos + big b.size) { + s := b.bufpos + big b.size; + if (sys->seek(b.fd, s, 0) != s) + return ERROR; + b.filpos = s; + } + i := len b.buffer - b.size - 1; + if(i > Bufsize) + i = Bufsize; + if ((i = sys->read(b.fd, b.buffer[b.size:], i)) <= 0) { + if(i < 0) + return ERROR; + return EOF; + } + b.size += i; + b.filpos += big i; + return i; +} + +writechunk(b: ref Iobuf): int +{ + if (b.fd == nil) + return ERROR; + if (b.filpos != b.bufpos) { + if (sys->seek(b.fd, b.bufpos, 0) != b.bufpos) + return ERROR; + b.filpos = b.bufpos; + } + if ((size := b.size) > Bufsize) + size = Bufsize; + if (sys->write(b.fd, b.buffer, size) != size) + return ERROR; + b.filpos += big size; + b.size -= size; + if (b.size) { + b.dirty = 1; + b.buffer[0:] = b.buffer[Bufsize:Bufsize+b.size]; + } else + b.dirty = 0; + b.bufpos += big size; + b.index -= size; + return size; +} + +Iobuf.close(b: self ref Iobuf) +{ + if (b.fd == nil) { + nofill(b); + return; + } + if (b.dirty) + b.flush(); + b.fd = nil; + b.buffer = nil; +} + +Iobuf.flush(b: self ref Iobuf): int +{ + if (b.fd == nil) + return ERROR; + if (b.lastop == OREAD){ + b.bufpos = b.filpos; + b.size = 0; + return 0; + } + while (b.dirty) { + if (writechunk(b) < 0) + return ERROR; + if (b.index < 0) { + b.bufpos += big b.index; + b.index = 0; + } + } + return 0; +} + +Iobuf.seek(b: self ref Iobuf, off: big, start: int): big +{ + npos: big; + + if (b.fd == nil){ + if(filler(b) != nil) + return big ERROR; + } + case (start) { + 0 => # absolute address + npos = off; + 1 => # offset from current location + npos = b.bufpos + big b.index + off; + off = npos; + start = Sys->SEEKSTART; + 2 => # offset from EOF + npos = big -1; + * => return big ERROR; + } + if (b.bufpos <= npos && npos < b.bufpos + big b.size) { + b.index = int(npos - b.bufpos); + return npos; + } + if (b.fd == nil || b.dirty && b.flush() < 0) + return big ERROR; + b.size = 0; + b.index = 0; + if ((s := sys->seek(b.fd, off, start)) < big 0) { + b.filpos = b.bufpos = big 0; + return big ERROR; + } + b.bufpos = b.filpos = s; + return b.bufpos = b.filpos = s; +} + +Iobuf.offset(b: self ref Iobuf): big +{ + return b.bufpos + big b.index; +} + +write2read(b: ref Iobuf): int +{ + while (b.dirty) + if (b.flush() < 0) + return ERROR; + b.bufpos = b.filpos; + b.size = 0; + b.lastop = OREAD; + if ((r := readchunk(b)) < 0) + return r; + if (b.index > b.size) + return EOF; + return 0; +} + +Iobuf.read(b: self ref Iobuf, buf: array of byte, n: int): int +{ + if (b.mode == OWRITE) + return ERROR; + if (b.lastop != OREAD){ + if ((r := write2read(b)) < 0) + return r; + } + k := n; + while (b.size - b.index < k) { + buf[0:] = b.buffer[b.index:b.size]; + buf = buf[b.size - b.index:]; + k -= b.size - b.index; + + b.bufpos += big b.size; + b.index = 0; + b.size = 0; + if ((r := readchunk(b)) < 0) { + if(r == EOF || n != k) + return n-k; + return ERROR; + } + } + buf[0:] = b.buffer[b.index:b.index+k]; + b.index += k; + return n; +} + +Iobuf.getb(b: self ref Iobuf): int +{ + if(b.lastop != OREAD){ + if(b.mode == OWRITE) + return ERROR; + if((r := write2read(b)) < 0) + return r; + } + if (b.index == b.size) { + b.bufpos += big b.index; + b.index = 0; + b.size = 0; + if ((r := readchunk(b)) < 0) + return r; + } + return int b.buffer[b.index++]; +} + +Iobuf.ungetb(b: self ref Iobuf): int +{ + if(b.mode == OWRITE || b.lastop != OREAD) + return ERROR; + b.index--; + return 1; +} + +Iobuf.getc(b: self ref Iobuf): int +{ + r, i, s: int; + + if(b.lastop != OREAD){ + if(b.mode == OWRITE) + return ERROR; + if((r = write2read(b)) < 0) + return r; + } + for(;;) { + if(b.index < b.size) { + r = int b.buffer[b.index]; + if(r < UTFself){ + b.index++; + return r; + } + (r, i, s) = sys->byte2char(b.buffer[0:b.size], b.index); + if (i != 0) { + b.index += i; + return r; + } + b.buffer[0:] = b.buffer[b.index:b.size]; + } + b.bufpos += big b.index; + b.size -= b.index; + b.index = 0; + if ((r = readchunk(b)) < 0) + return r; + } + # Not reached: + return -1; +} + +Iobuf.ungetc(b: self ref Iobuf): int +{ + if(b.index == 0 || b.mode == OWRITE || b.lastop != OREAD) + return ERROR; + stop := b.index - Sys->UTFmax; + if(stop < 0) + stop = 0; + buf := b.buffer[0:b.size]; + for(i := b.index-1; i >= stop; i--){ + (r, n, s) := sys->byte2char(buf, i); + if(s && i + n == b.index){ + b.index = i; + return 1; + } + } + b.index--; + + return 1; +} + +# optimised when term < UTFself (common case) +tgets(b: ref Iobuf, t: int): string +{ + str: string; + term := byte t; + for(;;){ + start := b.index; + end := start + sys->utfbytes(b.buffer[start:], b.size-start); + buf := b.buffer; + # XXX could speed up by adding extra byte to end of buffer and + # placing a sentinel there (eliminate one test, perhaps 35% speedup). + # (but not when we've been given the buffer externally) + for(i := start; i < end; i++){ + if(buf[i] == term){ + i++; + str += string buf[start:i]; + b.index = i; + return str; + } + } + str += string buf[start:i]; + if(i < b.size) + b.buffer[0:] = buf[i:b.size]; + b.size -= i; + b.bufpos += big i; + b.index = 0; + if(readchunk(b) < 0) + break; + } + return str; +} + +Iobuf.gets(b: self ref Iobuf, term: int): string +{ + i: int; + + if(b.mode == OWRITE) + return nil; + if(b.lastop != OREAD && write2read(b) < 0) + return nil; +# if(term < UTFself) +# return tgets(b, term); + str: string; + ch := -1; + for (;;) { + start := b.index; + n := 0; + while(b.index < b.size){ + (ch, i, nil) = sys->byte2char(b.buffer[0:b.size], b.index); + if(i == 0) # too few bytes for full Rune + break; + n += i; + b.index += i; + if(ch == term) + break; + } + if(n > 0) + str += string b.buffer[start:start+n]; + if(ch == term) + return str; + b.buffer[0:] = b.buffer[b.index:b.size]; + b.bufpos += big b.index; + b.size -= b.index; + b.index = 0; + if (readchunk(b) < 0) + break; + } + return str; # nil at EOF +} + +Iobuf.gett(b: self ref Iobuf, s: string): string +{ + r := ""; + if (b.mode == OWRITE || (ch := b.getc()) < 0) + return nil; + do { + r[len r] = ch; + for (i:=0; i<len(s); i++) + if (ch == s[i]) + return r; + } while ((ch = b.getc()) >= 0); + return r; +} + +read2write(b: ref Iobuf) +{ + # last operation was a read + b.bufpos += big b.index; + b.size = 0; + b.index = 0; + b.lastop = OWRITE; +} + +Iobuf.write(b: self ref Iobuf, buf: array of byte, n: int): int +{ + if(b.lastop != OWRITE) { + if(b.mode == OREAD) + return ERROR; + read2write(b); + } + start := 0; + k := n; + while(k > 0){ + nw := Bufsize - b.index; + if(nw > k) + nw = k; + end := start + nw; + b.buffer[b.index:] = buf[start:end]; + start = end; + b.index += nw; + k -= nw; + if(b.index > b.size) + b.size = b.index; + b.dirty = 1; + if(b.size == Bufsize && writechunk(b) < 0) + return ERROR; + } + return n; +} + +Iobuf.putb(b: self ref Iobuf, c: byte): int +{ + if(b.lastop != OWRITE) { + if(b.mode == OREAD) + return ERROR; + read2write(b); + } + b.buffer[b.index++] = c; + if(b.index > b.size) + b.size = b.index; + b.dirty = 1; + if(b.size >= Bufsize) { + if (b.fd == nil) + return ERROR; + if (writechunk(b) < 0) + return ERROR; + } + return 0; +} + +Iobuf.putc(b: self ref Iobuf, c: int): int +{ + if(b.lastop != OWRITE) { + if (b.mode == OREAD) + return ERROR; + read2write(b); + } + if(c < UTFself) + b.buffer[b.index++] = byte c; + else + b.index += sys->char2byte(c, b.buffer, b.index); + if (b.index > b.size) + b.size = b.index; + b.dirty = 1; + if (b.size >= Bufsize) { + if (writechunk(b) < 0) + return ERROR; + } + return 0; +} + +Iobuf.puts(b: self ref Iobuf, s: string): int +{ + if(b.lastop != OWRITE) { + if (b.mode == OREAD) + return ERROR; + read2write(b); + } + n := len s; + if (n == 0) + return 0; + ind := b.index; + buf := b.buffer; + for(i := 0; i < n; i++){ + c := s[i]; + if(c < UTFself) + buf[ind++] = byte c; + else + ind += sys->char2byte(c, buf, ind); + if(ind >= Bufsize){ + b.index = ind; + if(ind > b.size) + b.size = ind; + b.dirty = 1; + if(writechunk(b) < 0) + return ERROR; + ind = b.index; + } + } + b.dirty = b.index != ind; + b.index = ind; + if (ind > b.size) + b.size = ind; + return n; +} + +filler(b: ref Iobuf): ref Filler +{ + for (f := fillers; f != nil; f = f.next) + if(f.iobuf == b) + return f; + return nil; +} + +Iobuf.setfill(b: self ref Iobuf, fill: BufioFill) +{ + if ((f := filler(b)) != nil) + f.fill = fill; + else + fillers = ref Filler(b, fill, fillers); +} + +nofill(b: ref Iobuf) +{ + prev: ref Filler; + for(f := fillers; f != nil; f = f.next) { + if(f.iobuf == b) { + if (prev == nil) + fillers = f.next; + else + prev.next = f.next; + } + prev = f; + } +} |
