// ------------------------------------------------------------------------ // Project atila // Active Thing (activething.com) git.activething.com/go // // File name buffer.go // Created by DEV // Modified 16/01/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ package bins import ( "activething.com/go/gogo/core/bins/chars" "bytes" "io" "unicode/utf8" ) const ( maxInt = int(^uint(0) >> 1) MinRead = 512 ) type ( Buffer struct { data []byte off int ini [64]byte lastOp bufferOp } ) func NewBuffer() *Buffer { return &Buffer{} } func (b *Buffer) Len() int { return len(b.data) - b.off } func (b *Buffer) Cap() int { return cap(b.data) } func (b *Buffer) isEmpty() bool { return len(b.data) <= b.off } func (b *Buffer) Bytes() []byte { return b.data[b.off:] } func (b *Buffer) String() string { if b == nil { return "" } return string(b.data[b.off:]) } func (b *Buffer) Reset() { b.data = b.data[:0] b.off = 0 b.lastOp = opInvalid } func (b *Buffer) Truncate(size int) { if size == 0 { b.Reset() return } b.lastOp = opInvalid if size < 0 || size > b.Len() { panic(G.ErrOutOfRange) } b.data = b.data[:b.off+size] } func (b *Buffer) Read(data []byte) (count int, err error) { b.lastOp = opInvalid if b.isEmpty() { b.Reset() if len(data) == 0 { return 0, nil } return 0, io.EOF } count = copy(data, b.data[b.off:]) b.off += count if count > 0 { b.lastOp = opRead } return count, nil } func (b *Buffer) ReadFrom(reader io.Reader) (count int64, err error) { b.lastOp = opInvalid for { i := b.tryGrowByStrategy(MinRead) b.data = b.data[:i] m, e := reader.Read(b.data[i:cap(b.data)]) if m < 0 { panic(G.ErrNegativeRead) } b.data = b.data[:i+m] count += int64(m) if e == io.EOF { return count, nil } if e != nil { return count, e } } } func (b *Buffer) ReadByte() (byte, error) { if b.isEmpty() { b.Reset() return 0, io.EOF } c := b.data[b.off] b.off++ b.lastOp = opRead return c, nil } func (b *Buffer) ReadBytes(delimiter byte) (result []byte, er error) { slice, er := b.readSlice(delimiter) result = append(result, slice...) return result, er } func (b *Buffer) ReadString(delimiter byte) (result string, err error) { slice, err := b.readSlice(delimiter) return string(slice), err } func (b *Buffer) ReadRune() (run rune, size int, err error) { if b.isEmpty() { b.Reset() return 0, 0, io.EOF } c := b.data[b.off] if c < utf8.RuneSelf { b.off++ b.lastOp = opRune return rune(c), 1, nil } r, n := utf8.DecodeRune(b.data[b.off:]) b.off += n b.lastOp = bufferOp(n) return r, n, nil } func (b *Buffer) readSlice(delimiter byte) (ln []byte, er error) { i := bytes.IndexByte(b.data[b.off:], delimiter) end := b.off + i + 1 if i < 0 { end = len(b.data) er = io.EOF } ln = b.data[b.off:end] b.off = end b.lastOp = opRead return ln, er } func (b *Buffer) UnreadRune() error { if b.lastOp <= opInvalid { return G.ErrUnreadInvalid } if b.off >= int(b.lastOp) { b.off -= int(b.lastOp) } b.lastOp = opInvalid return nil } func (b *Buffer) UnreadByte() error { if b.lastOp == opInvalid { return G.ErrUnreadInvalid } b.lastOp = opInvalid if b.off > 0 { b.off-- } return nil } func (b *Buffer) AppendWith(appender Appender) { b.lastOp = opInvalid if appender != nil { b.data = appender.Append(b.data) } } func (b *Buffer) Write(value []byte) (int, error) { b.lastOp = opInvalid return copy(b.data[b.tryGrow(len(value)):], value), nil } func (b *Buffer) W (value []byte) { b.lastOp = opInvalid copy(b.data[b.tryGrow(len(value)):], value) } // WOQ With Optional Quoted func (b *Buffer) WOQ (add bool, quote byte, value []byte) { b.lastOp = opInvalid if !add { copy(b.data[b.tryGrow(len(value)):], value) return } b.WriteByte(quote) sz := b.tryGrow(len(value)+2) copy(b.data[sz:],value) b.WriteByte(quote) } func (b *Buffer) WriteHTML(source []byte) { st := 0 for i,c := range source { if c == '<' || c == '>' || c == '&' { if st < i { b.W(source[st:i]) } b.WriteString(`\u00`) b.WriteByte(chars.G.HEX[c>>4]) b.WriteByte(chars.G.HEX[c&0xF]) st = i + 1 } if c == 0xE2 && i+2 < len(source) && source[i+1] == 0x80 && source[i+2]&^1 == 0xA8 { if st < i { b.W(source[st:i]) } b.WriteString(`\u202`) b.WriteByte(chars.G.HEX[source[i+2]&0xF]) st = i + 3 } if st < len(source) { b.W(source[st:]) } } } func (b *Buffer) WriteByte(value byte) { b.lastOp = opInvalid b.data[b.tryGrow(1)] = value } func (b *Buffer) Write2Byte(valueA, valueB byte) { b.lastOp = opInvalid p := b.tryGrow(2) b.data[p] = valueA b.data[p+1] = valueB } func (b *Buffer) WriteString(value string) { b.W([]byte(value)) } func (b *Buffer) WriteTo(writer io.Writer) (int64, error) { return b.writeTo(writer, false) } func (b *Buffer) FlushTo(writer io.Writer) (int64, error) { return b.writeTo(writer, true) } func (b *Buffer) writeTo(writer io.Writer, reset bool) (count int64, err error) { if reset { b.lastOp = opInvalid } if nBytes := b.Len(); nBytes > 0 { m, e := writer.Write(b.data[b.off:]) if m > nBytes { panic(G.ErrWriteCount) } b.off += m count = int64(m) if e != nil { return count, e } if m != nBytes { return count, G.ErrTooShort } } if reset { b.Reset() } return count, nil } func (b *Buffer) tryGrow(size int) int { m := len(b.data) if size <= cap(b.data)-m { b.data = b.data[:m+size] return m } m = b.Len() if m == 0 && b.off != 0 { b.Reset() } if i, ok := b.tryGrowByResize(size); ok { return i } if b.data == nil && size <= len(b.ini) { b.data = b.ini[:size] return 0 } c := cap(b.data) if size <= c/2-m { copy(b.data, b.data[b.off:]) } else if c > maxInt-c-size { panic(G.ErrTooLarge) } else { buf := makeSlice(2*c + size) copy(buf, b.data[b.off:]) b.data = buf } b.off = 0 b.data = b.data[:m+size] return m } func (b *Buffer) tryGrowByResize(size int) (int, bool) { if l := len(b.data); size <= cap(b.data)-l { b.data = b.data[:l+size] return l, true } return 0, false } func (b *Buffer) tryGrowByStrategy(size int) int { m := len(b.data) if m == 0 && b.off != 0 { b.Reset() } if i, ok := b.tryGrowByResize(size); ok { return i } if b.data == nil && size <= len(b.ini) { b.data = b.ini[:size] return 0 } c := cap(b.data) if size <= c/2-m { copy(b.data, b.data[b.off:]) } else if c > maxInt-c-size { panic(G.ErrTooLarge) } else { buf := makeSlice(2*c + size) copy(buf, b.data[b.off:]) b.data = buf } b.off = 0 b.data = b.data[:m+size] return m } func makeSlice(size int) (result []byte) { dn := false defer func(d *bool) { if !(*d) && recover() != nil { panic(G.ErrTooLarge) } }(&dn) result = make([]byte, size) dn = true return }