From 5001bccc82efdf1b8a71ed524548238a2d4500af Mon Sep 17 00:00:00 2001 From: Mikhail Knyazhev Date: Sun, 12 Jan 2025 10:17:27 +0300 Subject: [PATCH] add buffer --- .golangci.yml | 1 - LICENSE | 2 +- Makefile | 4 +- ascii.go | 2 +- cache/cache_simple.go | 5 + cache/cache_time.go | 5 + cache/cache_time_test.go | 5 + cache/common.go | 5 + codec/encdec.go | 6 +- codec/encdec_blob.go | 6 +- codec/encdec_blob_test.go | 3 +- codec/encdec_file.go | 2 +- codec/encdec_file_test.go | 2 +- codec/map_merge.go | 31 ++-- codec/map_merge_test.go | 14 +- data/buffer.go | 302 ++++++++++++++++++++++++++++++++ data/buffer_test.go | 357 ++++++++++++++++++++++++++++++++++++++ fs/files.go | 2 +- hash/hash.go | 2 +- ioutils.go | 95 +--------- ioutils_test.go | 6 +- pool/pool.go | 2 +- pool/pool_test.go | 2 +- pool/slice.go | 5 + shell/shell.go | 2 +- shell/shell_test.go | 2 +- 26 files changed, 738 insertions(+), 132 deletions(-) create mode 100644 data/buffer.go create mode 100644 data/buffer_test.go diff --git a/.golangci.yml b/.golangci.yml index 3228670..15616bf 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -368,7 +368,6 @@ linters: - errorlint - bodyclose - exportloopref - - gci - gosec - lll fast: false diff --git a/LICENSE b/LICENSE index 1fd5dac..2a1ad25 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2024, Mikhail Knyazhev +Copyright (c) 2024-2025, Mikhail Knyazhev Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Makefile b/Makefile index 918d40d..b464f86 100644 --- a/Makefile +++ b/Makefile @@ -23,8 +23,8 @@ build: tests: devtool test -.PHONY: pre-commite -pre-commite: setup lint build tests +.PHONY: pre-commit +pre-commit: setup license lint build tests .PHONY: ci ci: install setup lint build tests diff --git a/ascii.go b/ascii.go index aa8bde3..8c8ea28 100644 --- a/ascii.go +++ b/ascii.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/cache/cache_simple.go b/cache/cache_simple.go index a1721b7..2adb7a6 100644 --- a/cache/cache_simple.go +++ b/cache/cache_simple.go @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + package cache import ( diff --git a/cache/cache_time.go b/cache/cache_time.go index f65aede..9a31468 100644 --- a/cache/cache_time.go +++ b/cache/cache_time.go @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + package cache import ( diff --git a/cache/cache_time_test.go b/cache/cache_time_test.go index 879d0fe..cc4f20e 100644 --- a/cache/cache_time_test.go +++ b/cache/cache_time_test.go @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + package cache import ( diff --git a/cache/common.go b/cache/common.go index df5de02..36c56d6 100644 --- a/cache/common.go +++ b/cache/common.go @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + package cache import "time" diff --git a/codec/encdec.go b/codec/encdec.go index 12b5caa..91fd09b 100644 --- a/codec/encdec.go +++ b/codec/encdec.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ @@ -31,7 +31,7 @@ type ( Codec struct { Encode func(in interface{}) ([]byte, error) Decode func(b []byte, out interface{}) error - Merge func(dst map[string]interface{}, src map[string]interface{}) error + Merge func(dst map[string]interface{}, src ...map[string]interface{}) } encoders struct { list map[string]Codec @@ -54,7 +54,7 @@ func (v *encoders) Add( ext string, enc func(interface{}) ([]byte, error), dec func([]byte, interface{}) error, - merge func(map[string]interface{}, map[string]interface{}) error, + merge func(map[string]interface{}, ...map[string]interface{}), ) *encoders { v.mux.Lock(func() { v.list[ext] = Codec{ diff --git a/codec/encdec_blob.go b/codec/encdec_blob.go index 465167d..75f5a77 100644 --- a/codec/encdec_blob.go +++ b/codec/encdec_blob.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ @@ -53,9 +53,7 @@ func (v *BlobEncoder) Encode(configs ...interface{}) error { if err = codec.Decode(bb, &tmp); err != nil { return err } - if err = codec.Merge(out, tmp); err != nil { - return err - } + codec.Merge(out, tmp) } v.Blob, err0 = codec.Encode(out) return err0 diff --git a/codec/encdec_blob_test.go b/codec/encdec_blob_test.go index 7d9c911..6e24142 100644 --- a/codec/encdec_blob_test.go +++ b/codec/encdec_blob_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ @@ -9,6 +9,7 @@ import ( "testing" "go.osspkg.com/casecheck" + "go.osspkg.com/ioutils/codec" ) diff --git a/codec/encdec_file.go b/codec/encdec_file.go index a5e72da..d55ba9c 100644 --- a/codec/encdec_file.go +++ b/codec/encdec_file.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/codec/encdec_file_test.go b/codec/encdec_file_test.go index 6d068c3..fe1f66f 100644 --- a/codec/encdec_file_test.go +++ b/codec/encdec_file_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/codec/map_merge.go b/codec/map_merge.go index a986388..0c6ed2a 100644 --- a/codec/map_merge.go +++ b/codec/map_merge.go @@ -1,24 +1,27 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ package codec -func mapMerge(dst map[string]interface{}, src map[string]interface{}) error { - for k, v := range src { - vv, ok := dst[k] - if !ok { +func mapMerge(dst map[string]interface{}, src ...map[string]interface{}) { + for _, next := range src { + for k, v := range next { + vv, ok := dst[k] + if !ok { + dst[k] = v + continue + } + + m1, ok1 := vv.(map[string]interface{}) + m2, ok2 := v.(map[string]interface{}) + if ok2 && ok1 { + mapMerge(m1, m2) + continue + } + dst[k] = v - continue } - vMap, vOk := v.(map[string]interface{}) - vvMap, vvOk := vv.(map[string]interface{}) - if vOk && vvOk { - return mapMerge(vvMap, vMap) - } - dst[k] = v } - - return nil } diff --git a/codec/map_merge_test.go b/codec/map_merge_test.go index 7b27620..3f89ab8 100644 --- a/codec/map_merge_test.go +++ b/codec/map_merge_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ @@ -17,6 +17,8 @@ func TestUnit_mapMerge(t *testing.T) { "aa": map[string]interface{}{ "bb": "cc", }, + "yy": 123, + "ww": 123, } mapB := map[string]interface{}{ "zz": "xx", @@ -26,10 +28,15 @@ func TestUnit_mapMerge(t *testing.T) { "rr": "tt", }, }, + "ww": map[string]interface{}{ + "gg": "hh", + }, } - casecheck.NoError(t, mapMerge(mapA, mapB)) + mapMerge(mapA, mapB) + casecheck.Equal(t, map[string]interface{}{ + "yy": 123, "zz": "xx", "qq": "ww", "aa": map[string]interface{}{ @@ -39,5 +46,8 @@ func TestUnit_mapMerge(t *testing.T) { "rr": "tt", }, }, + "ww": map[string]interface{}{ + "gg": "hh", + }, }, mapA) } diff --git a/data/buffer.go b/data/buffer.go new file mode 100644 index 0000000..448c29e --- /dev/null +++ b/data/buffer.go @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package data + +import ( + "bytes" + "errors" + "fmt" + "io" + "unicode/utf8" +) + +const buffSize = 512 + +type Buffer struct { + buf []byte + pos int +} + +func NewBuffer(size int) *Buffer { + return &Buffer{ + buf: make([]byte, 0, size), + pos: 0, + } +} + +func (v *Buffer) Reset() { + for i := 0; i < v.Size(); i++ { + v.buf[i] = 0 + } + v.buf = v.buf[:0] + v.pos = 0 +} + +func (v *Buffer) Bytes() []byte { + return v.buf[:] +} + +func (v *Buffer) String() string { + return string(v.Bytes()) +} + +func (v *Buffer) Size() int { + return len(v.buf) +} + +func (v *Buffer) Len() int { + return len(v.buf) - v.pos +} + +func (v *Buffer) Truncate(c int) { + if c <= 0 { + return + } + n := v.Size() - c + if n <= 0 { + v.Reset() + return + } + + v.buf = v.buf[:n] + + for i := 1; i <= 3 && n-i >= 0; i++ { + off := n - i + if utf8.RuneStart(v.buf[off]) { + if v.buf[off] > utf8.RuneSelf { + n -= i + } + break + } + } + + v.buf = v.buf[:n] + + if v.pos > n { + v.pos = n + } +} + +func (v *Buffer) Write(p []byte) (int, error) { + v.buf = append(v.buf, p...) + return len(p), nil +} + +func (v *Buffer) WriteString(s string) (int, error) { + return v.Write([]byte(s)) +} + +func (v *Buffer) WriteByte(b byte) error { + v.buf = append(v.buf, b) + return nil +} + +func (v *Buffer) WriteRune(r rune) (n int, err error) { + n = v.Size() + v.buf = utf8.AppendRune(v.buf, r) + n = v.Size() - n + return +} + +func (v *Buffer) WriteTo(w io.Writer) (int64, error) { + if v.Len() <= 0 { + return 0, io.EOF + } + + n, err := w.Write(v.buf[v.pos:]) + if err != nil { + return 0, err + } + v.pos += n + return int64(n), nil +} + +func (v *Buffer) WriteAt(b []byte, off int64) (int, error) { + if off < 0 { + off = 0 + } + + if len(b)+int(off) > v.Size() { + v.buf = append(v.buf[:off], b...) + } else { + copy(v.buf[off:], b[:]) + } + + return len(b), nil +} + +func (v *Buffer) ReadFrom(r io.Reader) (int64, error) { + n := 0 + b := make([]byte, buffSize) + for { + m, err := r.Read(b) + if m < 0 { + return 0, fmt.Errorf("negative read bytes") + } + if err != nil && !errors.Is(err, io.EOF) { + return 0, err + } + n += m + v.buf = append(v.buf, b[:m]...) + if m < buffSize || errors.Is(err, io.EOF) { + break + } + } + return int64(n), nil +} + +func (v *Buffer) Read(p []byte) (int, error) { + if len(p) == 0 { + return 0, fmt.Errorf("got zero buffer arg") + } + if v.Len() == 0 { + return 0, io.EOF + } + n := copy(p[:], v.buf[v.pos:]) + v.pos += n + return n, nil +} + +func (v *Buffer) ReadAt(p []byte, off int64) (int, error) { + if len(p) == 0 { + return 0, fmt.Errorf("got zero buffer arg") + } + if off < 0 || int(off) >= v.Size() { + return 0, io.EOF + } + n := copy(p[:], v.buf[int(off):]) + return n, nil +} + +func (v *Buffer) Next(n int) []byte { + if n <= 0 { + return nil + } + m := v.Len() + if m == 0 { + return nil + } + if n > m { + n = m + } + b := make([]byte, n) + v.pos += copy(b[:], v.buf[v.pos:]) + return b +} + +func (v *Buffer) ReadByte() (byte, error) { + m := v.Len() + if m == 0 { + return 0, io.EOF + } + b := v.buf[v.pos] + v.pos++ + return b, nil +} + +func (v *Buffer) UnreadByte() error { + if v.pos <= 0 { + return fmt.Errorf("at beginning") + } + v.pos-- + return nil +} + +func (v *Buffer) ReadRune() (rune, int, error) { + m := v.Len() + if m == 0 { + return 0, 0, io.EOF + } + + r, n := utf8.DecodeRune(v.buf[v.pos:]) + v.pos += n + return r, n, nil +} + +func (v *Buffer) UnreadRune() error { + if v.pos <= 0 { + return fmt.Errorf("at beginning") + } + + n := v.pos + + for i := 1; i <= 4 && n-i >= 0; i++ { + off := n - i + if utf8.RuneStart(v.buf[off]) { + n -= i + break + } + } + + v.pos -= n + + return nil +} + +func (v *Buffer) ReadBytes(delim byte) ([]byte, error) { + m := v.Len() + if m == 0 { + return nil, io.EOF + } + i := bytes.IndexByte(v.buf[v.pos:], delim) + end := v.pos + i + 1 + if i < 0 { + end = v.Size() + } + b := v.buf[v.pos:end] + v.pos = end + return b, nil +} + +func (v *Buffer) ReadSubBytes(delim string) ([]byte, error) { + m := v.Len() + if m == 0 { + return nil, io.EOF + } + i := bytes.Index(v.buf[v.pos:], []byte(delim)) + end := v.pos + i + len(delim) + if i < 0 { + end = v.Size() + } + b := v.buf[v.pos:end] + v.pos = end + return b, nil +} + +func (v *Buffer) ReadString(delim byte) (string, error) { + b, err := v.ReadBytes(delim) + if err != nil { + return "", err + } + return string(b), nil +} + +func (v *Buffer) ReadSubString(delim string) (string, error) { + b, err := v.ReadSubBytes(delim) + if err != nil { + return "", err + } + return string(b), nil +} + +func (v *Buffer) Seek(offset int64, whence int) (int64, error) { + switch whence { + case 0: + v.pos = int(offset) + case 1: + v.pos += int(offset) + case 2: + v.pos = v.Size() + int(offset) + default: + return 0, fmt.Errorf("invalid whence") + } + if v.pos < 0 { + v.pos = 0 + } else if v.pos > v.Size() { + v.pos = v.Size() + } + return int64(v.pos), nil +} diff --git a/data/buffer_test.go b/data/buffer_test.go new file mode 100644 index 0000000..c2cbed6 --- /dev/null +++ b/data/buffer_test.go @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package data + +import ( + "bytes" + "fmt" + "io" + "testing" + + "go.osspkg.com/casecheck" +) + +type mockFailReader struct { + Err error + N int +} + +func (v *mockFailReader) Read(p []byte) (int, error) { + if v.Err != nil { + return 0, v.Err + } + return v.N, nil +} +func (v *mockFailReader) Write(p []byte) (int, error) { + if v.Err != nil { + return 0, v.Err + } + return v.N, nil +} + +func TestUnit_Data1(t *testing.T) { + d := NewBuffer(10) + + n, err := d.WriteString("aФ卉**") + casecheck.NoError(t, err) + casecheck.Equal(t, 9, n) + + casecheck.Error(t, d.UnreadRune()) + casecheck.Error(t, d.UnreadByte()) + + r, s, err := d.ReadRune() + casecheck.NoError(t, err) + casecheck.Equal(t, rune('a'), r) + casecheck.Equal(t, 1, s) + + r, s, err = d.ReadRune() + casecheck.NoError(t, err) + casecheck.Equal(t, rune('Ф'), r) + casecheck.Equal(t, 2, s) + + b, err := d.ReadByte() + casecheck.NoError(t, err) + casecheck.Equal(t, byte(240), b) + + casecheck.NoError(t, d.UnreadRune()) + + r, s, err = d.ReadRune() + casecheck.NoError(t, err) + casecheck.Equal(t, rune('Ф'), r) + casecheck.Equal(t, 2, s) + + r, s, err = d.ReadRune() + casecheck.NoError(t, err) + casecheck.Equal(t, rune('卉'), r) + casecheck.Equal(t, 4, s) + + bs, err := io.ReadAll(d) + casecheck.NoError(t, err) + casecheck.Equal(t, []byte("**"), bs) + + r, s, err = d.ReadRune() + casecheck.Error(t, err) + casecheck.Equal(t, rune(0), r) + casecheck.Equal(t, 0, s) + + b, err = d.ReadByte() + casecheck.Error(t, err) + casecheck.Equal(t, byte(0), b) + + casecheck.NoError(t, d.UnreadByte()) + + b, err = d.ReadByte() + casecheck.NoError(t, err) + casecheck.Equal(t, byte(42), b) + + i, err := d.Seek(0, 0) + casecheck.NoError(t, err) + casecheck.Equal(t, int64(0), i) + + i, err = d.Seek(-5, 0) + casecheck.NoError(t, err) + casecheck.Equal(t, int64(0), i) + + i, err = d.Seek(5, 1) + casecheck.NoError(t, err) + casecheck.Equal(t, int64(5), i) + + i, err = d.Seek(5, 2) + casecheck.NoError(t, err) + casecheck.Equal(t, int64(9), i) + + i, err = d.Seek(5, 3) + casecheck.Error(t, err) + casecheck.Equal(t, int64(0), i) + + d.Seek(0, 0) + + str, err := d.ReadString('*') + casecheck.NoError(t, err) + casecheck.Equal(t, "aФ卉*", str) + + str, err = d.ReadString('*') + casecheck.NoError(t, err) + casecheck.Equal(t, "*", str) + + d.Seek(0, 0) + + str, err = d.ReadString('+') + casecheck.NoError(t, err) + casecheck.Equal(t, "aФ卉**", str) + + str, err = d.ReadString('+') + casecheck.Error(t, err) + casecheck.Equal(t, "", str) + + d.Seek(0, 0) + + str, err = d.ReadSubString("卉*") + casecheck.NoError(t, err) + casecheck.Equal(t, "aФ卉*", str) + + d.Seek(0, 0) + + str, err = d.ReadSubString("*卉") + casecheck.NoError(t, err) + casecheck.Equal(t, "aФ卉**", str) + + str, err = d.ReadSubString("*卉") + casecheck.Error(t, err) + casecheck.Equal(t, "", str) + + d.Seek(0, 0) + + bs = d.Next(3) + casecheck.NotNil(t, bs) + casecheck.Equal(t, []byte("aФ"), bs) + + bs = d.Next(3) + casecheck.NotNil(t, bs) + casecheck.Equal(t, []byte{240, 175, 160}, bs) + + bs = d.Next(0) + casecheck.Nil(t, bs) + + bs = d.Next(10) + casecheck.NotNil(t, bs) + casecheck.Equal(t, []byte{172, 42, 42}, bs) + + bs = d.Next(10) + casecheck.Nil(t, bs) + + d.Seek(0, 0) + + bs = make([]byte, 2) + + n, err = d.ReadAt(bs, 2) + casecheck.NoError(t, err) + casecheck.Equal(t, 2, n) + casecheck.Equal(t, []byte{164, 240}, bs) + + n, err = d.ReadAt(nil, 2) + casecheck.Error(t, err) + casecheck.Equal(t, 0, n) + + n, err = d.ReadAt(bs, 10) + casecheck.Error(t, err) + casecheck.Equal(t, 0, n) + + bb := bytes.NewBufferString("123") + + i, err = d.ReadFrom(bb) + casecheck.NoError(t, err) + casecheck.Equal(t, int64(3), i) + casecheck.Equal(t, "aФ卉**123", d.String()) + + i, err = d.ReadFrom(&mockFailReader{Err: fmt.Errorf("1")}) + casecheck.Error(t, err) + casecheck.Equal(t, int64(0), i) + + i, err = d.ReadFrom(&mockFailReader{N: -1}) + casecheck.Error(t, err) + casecheck.Equal(t, int64(0), i) + + d.Seek(0, 0) + + bb = bytes.NewBufferString("123") + i, err = d.WriteTo(bb) + casecheck.NoError(t, err) + casecheck.Equal(t, int64(12), i) + casecheck.Equal(t, "123aФ卉**123", bb.String()) + + i, err = d.WriteTo(bb) + casecheck.Error(t, err) + casecheck.Equal(t, int64(0), i) + + d.Seek(0, 0) + + i, err = d.WriteTo(&mockFailReader{Err: fmt.Errorf("1")}) + casecheck.Error(t, err) + casecheck.Equal(t, int64(0), i) + + casecheck.NoError(t, d.WriteByte('1')) + + n, err = d.WriteRune('Ф') + casecheck.NoError(t, err) + casecheck.Equal(t, 2, n) + + casecheck.Equal(t, "aФ卉**1231Ф", d.String()) + + d.Reset() + d.WriteString("00000") + n, err = d.WriteAt([]byte("11"), -1) + casecheck.NoError(t, err) + casecheck.Equal(t, 2, n) + casecheck.Equal(t, "11000", d.String()) + + d.Reset() + d.WriteString("00000") + n, err = d.WriteAt([]byte("11"), 2) + casecheck.NoError(t, err) + casecheck.Equal(t, 2, n) + casecheck.Equal(t, "00110", d.String()) + + d.Reset() + d.WriteString("00000") + n, err = d.WriteAt([]byte("11"), 4) + casecheck.NoError(t, err) + casecheck.Equal(t, 2, n) + casecheck.Equal(t, "000011", d.String()) + + d.Reset() + d.WriteString("00000") + n, err = d.WriteAt([]byte("11"), 10) + casecheck.NoError(t, err) + casecheck.Equal(t, 2, n) + casecheck.Equal(t, []byte{48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 49, 49}, d.Bytes()) +} + +func TestUnit_Data_Truncate(t *testing.T) { + tests := []struct { + in, out string + trc int + ra bool + s, l int + }{ + { + in: "aФ卉**", + out: "aФ卉**", + trc: 0, + ra: false, + s: 9, + l: 9, + }, + { + in: "aФ卉**", + out: "aФ卉*", + trc: 1, + ra: false, + s: 8, + l: 8, + }, + { + in: "aФ卉**", + out: "aФ卉", + trc: 2, + ra: false, + s: 7, + l: 7, + }, + { + in: "aФ卉**", + out: "aФ", + trc: 3, + ra: false, + s: 3, + l: 3, + }, + { + in: "aФ卉**", + out: "aФ", + trc: 5, + ra: false, + s: 3, + l: 3, + }, + { + in: "aФ卉**", + out: "a", + trc: 8, + ra: false, + s: 1, + l: 1, + }, + { + in: "aФ卉**", + out: "", + trc: 9, + ra: false, + s: 0, + l: 0, + }, + { + in: "aФ卉**", + out: "", + trc: 10, + ra: false, + s: 0, + l: 0, + }, + { + in: "aФ卉**", + out: "aФ", + trc: 3, + ra: true, + s: 3, + l: 0, + }, + } + for _, tt := range tests { + t.Run("", func(t *testing.T) { + d := NewBuffer(10) + _, err := d.WriteString(tt.in) + casecheck.NoError(t, err) + if tt.ra { + n, err := d.Read(nil) + casecheck.Error(t, err) + casecheck.Equal(t, 0, n) + + b, err := io.ReadAll(d) + casecheck.NoError(t, err) + casecheck.Equal(t, len(tt.in), len(b)) + + n, err = d.Read(b) + casecheck.Error(t, err) + casecheck.Equal(t, 0, n) + } + d.Truncate(tt.trc) + casecheck.Equal(t, tt.out, d.String()) + casecheck.Equal(t, tt.s, d.Size()) + casecheck.Equal(t, tt.l, d.Len()) + }) + } +} diff --git a/fs/files.go b/fs/files.go index 2c54fdc..5605756 100644 --- a/fs/files.go +++ b/fs/files.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/hash/hash.go b/hash/hash.go index e8ebaf5..6338c40 100644 --- a/hash/hash.go +++ b/hash/hash.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/ioutils.go b/ioutils.go index 46741a7..5cd283a 100644 --- a/ioutils.go +++ b/ioutils.go @@ -1,12 +1,11 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ package ioutils import ( - "bytes" "fmt" "io" @@ -22,101 +21,13 @@ func ReadAll(r io.ReadCloser) ([]byte, error) { return b, nil } -const buffSize = 128 - -var ( - ErrMaximumSize = errors.New("maximum buffer size reached") - ErrInvalidSize = errors.New("invalid size") -) - -func ReadFull(w io.Writer, r io.Reader, maxSize int) error { - if maxSize < 0 { - return ErrInvalidSize - } - - // nolint: ineffassign - var ( - total = 0 - n = 0 - buff = make([]byte, buffSize) - err error - ) - - for { - n, err = r.Read(buff) - if err != nil && !errors.Is(err, io.EOF) { - return err - } - if n < 0 { - return ErrInvalidSize - } - if _, err = w.Write(buff[:n]); err != nil { - return err - } - total += n - if maxSize > 0 && total > maxSize { - return ErrMaximumSize - } - if n < buffSize { - break - } - } - return nil -} - -func ReadBytes(v io.Reader, divide string) ([]byte, error) { - var ( - n int - err error - b = make([]byte, 0, 512) - db = []byte(divide) - dl = len(db) - ) - - for { - if len(b) == cap(b) { - b = append(b, 0)[:len(b)] - } - n, err = v.Read(b[len(b):cap(b)]) - b = b[:len(b)+n] - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - if len(b) < dl { - return b, io.EOF - } - if bytes.Equal(db, b[len(b)-dl:]) { - b = b[:len(b)-dl] - break - } - } - return b, nil -} - -func WriteBytes(v io.Writer, b []byte, divide string) error { - var ( - db = []byte(divide) - dl = len(db) - ) - if len(b) < dl || !bytes.Equal(db, b[len(b)-dl:]) { - b = append(b, db...) - } - if _, err := v.Write(b); err != nil { - return err - } - return nil -} - const copyBufferSize = 512 func Copy(w io.Writer, r io.Reader) (int, error) { - return CopyPack(w, r, copyBufferSize) + return CopyN(w, r, copyBufferSize) } -func CopyPack(w io.Writer, r io.Reader, size int) (int, error) { +func CopyN(w io.Writer, r io.Reader, size int) (int, error) { n := 0 buff := make([]byte, size) for { diff --git a/ioutils_test.go b/ioutils_test.go index edce270..916368c 100644 --- a/ioutils_test.go +++ b/ioutils_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ @@ -117,11 +117,11 @@ func TestUnit_Copy(t *testing.T) { casecheck.Equal(t, 521, len(bb)) } -func TestUnit_CopyPack(t *testing.T) { +func TestUnit_CopyN(t *testing.T) { b := make([]byte, 521, 1024) in := bytes.NewBuffer(b) out := bytes.NewBuffer(nil) - n, err := CopyPack(out, in, 1) + n, err := CopyN(out, in, 1) casecheck.NoError(t, err) casecheck.Equal(t, 521, n) bb, err := io.ReadAll(out) diff --git a/pool/pool.go b/pool/pool.go index d97681d..3ad93f3 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/pool/pool_test.go b/pool/pool_test.go index 7041a54..3e3cb77 100644 --- a/pool/pool_test.go +++ b/pool/pool_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/pool/slice.go b/pool/slice.go index 6b746de..27f34bb 100644 --- a/pool/slice.go +++ b/pool/slice.go @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + package pool func NewSlicePool[T any](l, c int) *Pool[*SlicePool[T]] { diff --git a/shell/shell.go b/shell/shell.go index 191d88b..1e9c607 100644 --- a/shell/shell.go +++ b/shell/shell.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ diff --git a/shell/shell_test.go b/shell/shell_test.go index 93d4229..3d7fdfe 100644 --- a/shell/shell_test.go +++ b/shell/shell_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Mikhail Knyazhev . All rights reserved. + * Copyright (c) 2024-2025 Mikhail Knyazhev . All rights reserved. * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */