v1.17.6
ADDED: * mapsx.(ListSortedKeys|ListSortedValues|SortedKeys|SortedValues)
This commit is contained in:
16 files changed
+960
-15
No files matched your search
@@ -0,0 +1 @@
|
||||
I need to test this still; I can't recall if it's complete or not
|
||||
@@ -0,0 +1,10 @@
|
||||
package binarystr
|
||||
|
||||
const (
|
||||
/*
|
||||
bufSz is the size of the buffer/chunk for encoders/decords to use when unspecified by the method caller.
|
||||
|
||||
The below is 64 KiB, which should be relatively reasonable.
|
||||
*/
|
||||
bufSz int = 65536
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Package binarystr parses and renders string (and other) representations of binary data.
|
||||
To distinguish this package from [encoding/binary], this package refers to this string encoding format
|
||||
as "bit encoding" or "bitstrings".
|
||||
|
||||
For example, the original data:
|
||||
|
||||
[]byte("test")
|
||||
|
||||
// Or:
|
||||
// []byte{0x74, 0x65, 0x73, 0x74}
|
||||
|
||||
can be encoded to a bitstring via e.g. [BytesToStr]:
|
||||
|
||||
01110100011001010111001101110100
|
||||
|
||||
And even formatted via [FmtBinStr]:
|
||||
|
||||
// For example, FmtBinStr("01110100011001010111001101110100", "", "", " "):
|
||||
01110100 01100101 01110011 01110100
|
||||
|
||||
// Or FmtBinStr("01110100011001010111001101110100", "", " ", "\n"):
|
||||
0111 0100
|
||||
0110 0101
|
||||
0111 0011
|
||||
0111 0100
|
||||
|
||||
Or can be converted to a series of "binary booleans" via e.g. [BytesToBool]:
|
||||
|
||||
[]bool{
|
||||
false, true, true, true,
|
||||
false, true, false, false,
|
||||
|
||||
false, true, true, false,
|
||||
false, true, false, true,
|
||||
|
||||
false, true, true, true,
|
||||
false, false, true, true,
|
||||
|
||||
false, true, true, true,
|
||||
false, true, false, false,
|
||||
}
|
||||
*/
|
||||
package binarystr
|
||||
@@ -0,0 +1,28 @@
|
||||
package binarystr
|
||||
|
||||
import (
|
||||
`errors`
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadNumOct error = errors.New("bad number of bits for even octet split")
|
||||
ErrNoChunk error = errors.New("empty/nil chunk")
|
||||
)
|
||||
|
||||
type (
|
||||
BadBinCharErr struct {
|
||||
posByte uint64
|
||||
posRune uint64
|
||||
r rune
|
||||
}
|
||||
|
||||
BadNumOctErr struct {
|
||||
posByte uint64
|
||||
posRune uint64
|
||||
rem string
|
||||
}
|
||||
|
||||
BadReadErr struct {
|
||||
numBytes uint64
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,408 @@
|
||||
package binarystr
|
||||
|
||||
import (
|
||||
`bytes`
|
||||
`errors`
|
||||
`io`
|
||||
`strconv`
|
||||
`strings`
|
||||
`unicode`
|
||||
)
|
||||
|
||||
/*
|
||||
BoolToBytes provides the inverse of [BytesToBool].
|
||||
|
||||
Each boolean in bools indicates a 0 (false) or 1 (true).
|
||||
|
||||
e.g.:
|
||||
|
||||
[]bool{
|
||||
false, true, true, true,
|
||||
false, true, false, false,
|
||||
|
||||
false, true, true, false,
|
||||
false, true, false, true,
|
||||
|
||||
false, true, true, true,
|
||||
false, false, true, true,
|
||||
|
||||
false, true, true, true,
|
||||
false, true, false, false,
|
||||
}
|
||||
|
||||
to:
|
||||
|
||||
[]byte{
|
||||
0x74, 0x65, 0x73, 0x74,
|
||||
}
|
||||
|
||||
If bools is nil, then b, rem, and err will also be nil and this function will no-op.
|
||||
|
||||
If len(bools) % 8 != 0, then err will be [ErrBadNumOct].
|
||||
The remaining booleans can be found in rem.
|
||||
*/
|
||||
func BoolToBytes(bools []bool) (b []byte, rem []bool, err error) {
|
||||
|
||||
var idx int
|
||||
var loc []bool
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
if bools == nil {
|
||||
return
|
||||
}
|
||||
if len(bools) == 0 {
|
||||
b = make([]byte, 0)
|
||||
return
|
||||
}
|
||||
|
||||
loc = make([]bool, len(bools))
|
||||
copy(loc, bools)
|
||||
|
||||
for idx = 0; idx < len(loc); idx += 8 {
|
||||
|
||||
if len(loc) < 8 {
|
||||
break
|
||||
}
|
||||
|
||||
writeBoolChunk(loc[idx:idx+8], sb)
|
||||
|
||||
loc = loc[idx+8:]
|
||||
}
|
||||
|
||||
if b, err = StrToBytes(sb.String()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(loc) > 1 {
|
||||
rem = append(rem, loc...)
|
||||
err = ErrBadNumOct
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
BytesToStr converts bytes to a bitstring.
|
||||
|
||||
e.g.:
|
||||
|
||||
[]byte{
|
||||
0x74, 0x65, 0x73, 0x74,
|
||||
}
|
||||
|
||||
to:
|
||||
|
||||
"01110100011001010111001101110100"
|
||||
|
||||
For the inverse, see [StrToBytes].
|
||||
To format the bitstring, see [FmtBinStr].
|
||||
*/
|
||||
func BytesToStr(b []byte) (binStr string) {
|
||||
|
||||
var cb byte
|
||||
var sb *strings.Builder
|
||||
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
|
||||
sb = new(strings.Builder)
|
||||
|
||||
for _, cb = range b {
|
||||
// This will never actually return an error.
|
||||
_, _ = sb.WriteString(
|
||||
strconv.FormatUint(
|
||||
uint64(uint8(cb)),
|
||||
2,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
binStr = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
BytesToBool returns a slice of "binary booleans".
|
||||
|
||||
For each bit of data in b, a 0 is false and a 1 is true (as one would expect).
|
||||
|
||||
For the inverse of this, see [BoolToBytes].
|
||||
*/
|
||||
func BytesToBool(b []byte) (bools []bool) {
|
||||
|
||||
var err error
|
||||
var r rune
|
||||
var rSz int
|
||||
var idx int
|
||||
var s *strings.Reader
|
||||
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// s will always be a valid bitstring.
|
||||
s = strings.NewReader(BytesToStr(b))
|
||||
|
||||
bools = make([]bool, s.Len())
|
||||
|
||||
for idx = 0; idx < s.Len(); idx++ {
|
||||
r, rSz, err = s.ReadRune()
|
||||
|
||||
if rSz == 0 {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
// It'll only be an io.EOF if anything at all.
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '0':
|
||||
bools[idx] = false
|
||||
case '1':
|
||||
bools[idx] = true
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
FmtBinStr formats bitstring s to more comfortable representations.
|
||||
|
||||
bitSep is a string to separate each bit boundary.
|
||||
|
||||
nybSep is a string to separate each "nibble"/"nybble" (4-bit "word") boundary.
|
||||
|
||||
bytSep is a string to separate each byte boundary.
|
||||
|
||||
Note that if you only explicitly use whitespace separators, then the output
|
||||
can still be parsed back to native []byte{} data via [StrToBytes].
|
||||
|
||||
Note that NO checking is done against the validity of s being a bitstring;
|
||||
the only content-checking done in s is if a given rune is a whitespace character or not
|
||||
(whitespace present in s is skipped over, which lets you run FmtBinStr to reformat a bitstring).
|
||||
*/
|
||||
func FmtBinStr(s, bitSep, nybSep, bytSep string) (out string) {
|
||||
|
||||
var idx int
|
||||
var lastIdx int
|
||||
var sb *strings.Builder
|
||||
|
||||
if strings.TrimSpace(s) == "" {
|
||||
return
|
||||
}
|
||||
|
||||
sb = new(strings.Builder)
|
||||
|
||||
lastIdx = len(s) - 1
|
||||
|
||||
for idx = 0; idx < len(s); idx++ {
|
||||
_ = sb.WriteByte(s[idx])
|
||||
if idx != lastIdx {
|
||||
if (idx+1)%8 == 0 {
|
||||
if bytSep != "" {
|
||||
_, _ = sb.WriteString(bytSep)
|
||||
}
|
||||
continue
|
||||
} else if (idx+1)%4 == 0 {
|
||||
if nybSep != "" {
|
||||
_, _ = sb.WriteString(nybSep)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if bitSep != "" {
|
||||
_, _ = sb.WriteString(bitSep)
|
||||
}
|
||||
}
|
||||
|
||||
out = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
NewDecoder returns a [Decoder] from [io.Reader] r.
|
||||
|
||||
Data from r is expected to be BITSTRING data (strings);
|
||||
doing read operations from a [Decoder] returns PLAIN data.
|
||||
*/
|
||||
func NewDecoder(r io.Reader) (e *Decoder) {
|
||||
|
||||
e = &Decoder{
|
||||
r: r,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
NewEncoder returns an [Encoder] from [io.Reader] r.
|
||||
|
||||
Data from r is expected to be PLAIN data;
|
||||
doing read operations from an [Encoder] returns BITSTRING data (strings).
|
||||
*/
|
||||
func NewEncoder(r io.Reader) (e *Encoder) {
|
||||
|
||||
e = &Encoder{
|
||||
r: r,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
StrToBytes converts a bitstring to bytes.
|
||||
|
||||
Whitespace is ignored.
|
||||
|
||||
e.g.:
|
||||
|
||||
"01110100011001010111001101110100"
|
||||
|
||||
to:
|
||||
|
||||
[]byte("test")
|
||||
|
||||
or:
|
||||
|
||||
[]byte{0x74, 0x65, 0x73, 0x74}
|
||||
|
||||
For the inverse, see [BytesToStr].
|
||||
|
||||
If the number of characters in binStr is not modulo 8 (`len() % 8 != 0`),
|
||||
then err will be of type [BadNumOctErr].
|
||||
|
||||
If there are any characters encountered in binStr that are not "0" or "1"
|
||||
(other than whitespace), then err will be of type [BadBinCharErr].
|
||||
*/
|
||||
func StrToBytes(binStr string) (b []byte, err error) {
|
||||
|
||||
var r rune
|
||||
var cb byte
|
||||
var rSz int
|
||||
var pos uint64
|
||||
var rErr error
|
||||
var posR uint64
|
||||
var chunk []rune
|
||||
var buf *strings.Reader
|
||||
var bytBuf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
defer func() {
|
||||
b = bytBuf.Bytes()
|
||||
}()
|
||||
|
||||
if strings.TrimSpace(binStr) == "" {
|
||||
return
|
||||
}
|
||||
buf = strings.NewReader(binStr)
|
||||
|
||||
chunk = make([]rune, 0, 8)
|
||||
for {
|
||||
r, rSz, rErr = buf.ReadRune()
|
||||
if rSz == 0 {
|
||||
break
|
||||
}
|
||||
pos += uint64(rSz)
|
||||
if rSz != 0 {
|
||||
posR++
|
||||
}
|
||||
if len(chunk) == 8 {
|
||||
if cb, err = chunkToByte(chunk); err != nil {
|
||||
return
|
||||
}
|
||||
if err = bytBuf.WriteByte(cb); err != nil {
|
||||
return
|
||||
}
|
||||
chunk = make([]rune, 0, 8)
|
||||
}
|
||||
if unicode.IsSpace(r) {
|
||||
continue
|
||||
}
|
||||
if rSz != 0 {
|
||||
switch r {
|
||||
case '0', '1':
|
||||
chunk = append(chunk, r)
|
||||
default:
|
||||
err = &BadBinCharErr{
|
||||
posByte: pos,
|
||||
posRune: posR,
|
||||
r: r,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if rErr != nil {
|
||||
if errors.Is(rErr, io.EOF) {
|
||||
break
|
||||
} else {
|
||||
err = rErr
|
||||
return
|
||||
}
|
||||
}
|
||||
if rSz == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(chunk) != 0 {
|
||||
if len(chunk) == 8 {
|
||||
if cb, err = chunkToByte(chunk); err != nil {
|
||||
return
|
||||
}
|
||||
if err = bytBuf.WriteByte(cb); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = &BadNumOctErr{
|
||||
posByte: pos,
|
||||
posRune: posR,
|
||||
rem: string(chunk),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func chunkToByte(chunk []rune) (b byte, err error) {
|
||||
|
||||
var u8 uint64
|
||||
|
||||
if len(chunk) == 0 {
|
||||
err = ErrNoChunk
|
||||
return
|
||||
}
|
||||
|
||||
if u8, err = strconv.ParseUint(string(chunk), 2, 8); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b = byte(uint8(u8))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func writeBoolChunk(chunk []bool, sb io.StringWriter) {
|
||||
|
||||
var b bool
|
||||
|
||||
if len(chunk) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, b = range chunk {
|
||||
switch b {
|
||||
case false:
|
||||
sb.WriteString("0")
|
||||
case true:
|
||||
sb.WriteString("1")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package binarystr
|
||||
|
||||
import "fmt"
|
||||
|
||||
// BytePos returns the byte position index immediately leading up to the error.
|
||||
func (b *BadBinCharErr) BytePos() (pos uint64) { return b.posByte }
|
||||
|
||||
// Rune returns the offending rune that caused the error.
|
||||
func (b *BadBinCharErr) Rune() (r rune) { return b.r }
|
||||
|
||||
// RunePos returns the rune (character) position index immediately leading up to the error.
|
||||
func (b *BadBinCharErr) RunePos() (pos uint64) { return b.posRune }
|
||||
|
||||
// Error returns the error message for a BadBinCharError. It conforms to an [error] interface.
|
||||
func (b *BadBinCharErr) Error() (err string) {
|
||||
|
||||
err = fmt.Sprintf(
|
||||
"rune/character after position index #%d (%#v) (byte #%d) is not \"0\" or \"1\"",
|
||||
b.posRune, b.r, b.posByte,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// String is an alias for the [BadBinCharErr.Error] method to conform explicitly to a [fmt.Stringer] interface.
|
||||
func (b *BadBinCharErr) String() (s string) { return b.Error() }
|
||||
@@ -0,0 +1,32 @@
|
||||
package binarystr
|
||||
|
||||
import "fmt"
|
||||
|
||||
// BytePos returns the byte position index immediately leading up to the error.
|
||||
func (b *BadNumOctErr) BytePos() (pos uint64) { return b.posByte }
|
||||
|
||||
// Error returns the error message for a BadNumOctError. It conforms to an [error] interface.
|
||||
func (b *BadNumOctErr) Error() (err string) {
|
||||
|
||||
err = fmt.Sprintf(
|
||||
"only able to read %d complete octets/bytes (%#d runes/characters); "+
|
||||
"%d remaining characters are less than 8 bits",
|
||||
b.posByte, b.posRune,
|
||||
len(b.rem),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Rem returns the remaining unparsed string of characters; this should ALWAYS be a length of 0 <= len(rem) < 8.
|
||||
(It will only be an empty string if [Decoder.Read] encountered the end of its source reader before the buffer was filled,
|
||||
but the available data cleanly broke into a last octet.)
|
||||
*/
|
||||
func (b *BadNumOctErr) Rem() (rem string) { return b.rem }
|
||||
|
||||
// RunePos returns the rune (character) position index immediately leading up to the error.
|
||||
func (b *BadNumOctErr) RunePos() (pos uint64) { return b.posRune }
|
||||
|
||||
// String is an alias for the [BadNumOctErr.Error] method to conform explicitly to a [fmt.Stringer] interface.
|
||||
func (b *BadNumOctErr) String() (s string) { return b.Error() }
|
||||
@@ -0,0 +1,16 @@
|
||||
package binarystr
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
)
|
||||
|
||||
// Error conforms a [BadReadErr] to an [error].
|
||||
func (b *BadReadErr) Error() (err string) {
|
||||
|
||||
err = fmt.Sprintf("only read %d bytes from internal read source before encountering a read error", b.numBytes)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NumBytesRead returns the number of bytes read (BEFORE mutation/encoding/decoding) before the error was hit.
|
||||
func (b *BadReadErr) NumBytesRead() (u64 uint64) { return b.numBytes }
|
||||
@@ -0,0 +1,116 @@
|
||||
package binarystr
|
||||
|
||||
import (
|
||||
`errors`
|
||||
`io`
|
||||
`strings`
|
||||
)
|
||||
|
||||
/*
|
||||
Read conforms to an [io.Reader].
|
||||
It returns plain data decoded from bitstring encoding in e's read source.
|
||||
|
||||
IMPORTANT: Read will return a [BadNumOctErr] if the amount of data in e's read source is not at least len(b) * 8!
|
||||
ONE BYTE in b equals EIGHT BYTES from e's read source!
|
||||
Likewise, n will ALWAYS be len(b), but the number of bytes actually read from e's internal reader
|
||||
will ALWAYS be n*8!
|
||||
In this case, b will still be populated with the available decoded data, and err ([BadNumOctErr])'s
|
||||
[BadNumOctErr.Rem] method will contain the remaining bitstring (if there is any left over) after decoding
|
||||
the octets that were returned (if any).
|
||||
|
||||
IMPORTANT: Read will return a [BadBinCharErr] if it encounters any character in e's read source that is not '0' or '1'!
|
||||
THIS INCLUDES WHITESPACE due to the buffer size calculation (unlike [StrToBytes]); it is NOT skipped!
|
||||
*/
|
||||
func (e *Decoder) Read(b []byte) (n int, err error) {
|
||||
|
||||
var inN int
|
||||
var s string
|
||||
var endIdx int
|
||||
var rem string
|
||||
var buf []byte
|
||||
|
||||
defer func() {
|
||||
if b != nil && buf != nil {
|
||||
s = BytesToStr(buf)
|
||||
copy(b, []byte(s))
|
||||
}
|
||||
}()
|
||||
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]byte, len(b)*8)
|
||||
if inN, err = e.r.Read(buf); err != nil {
|
||||
// This is caught/handled below.
|
||||
if errors.Is(err, io.EOF) {
|
||||
err = nil
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if inN < len(buf) {
|
||||
|
||||
}
|
||||
|
||||
if inN != len(buf) {
|
||||
err = &BadNumOctErr{
|
||||
posByte: uint64(inN),
|
||||
posRune: uint64(inN),
|
||||
rem: rem, // We hit the end too "early" so there is no "remaining".
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ReadString returns a bitstring-encoded [string] from the entirety of the data
|
||||
in the source reader of e.
|
||||
|
||||
IMPORTANT: If e's source reader is a non-terminating stream (e.g. a [math/rand.Rand]),
|
||||
this function will NEVER RETURN (and will likely consume all memory).
|
||||
YOU HAVE BEEN WARNED.
|
||||
|
||||
n is the number of bytes read from e's source reader.
|
||||
*/
|
||||
func (e *Decoder) ReadString() (s string, n uint64, err error) {
|
||||
|
||||
var inN int
|
||||
var buf []byte
|
||||
var rErr error
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
defer func() {
|
||||
if sb != nil {
|
||||
s = sb.String()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
buf = make([]byte, bufSz)
|
||||
inN, rErr = e.r.Read(buf)
|
||||
n += uint64(inN)
|
||||
|
||||
if inN != 0 {
|
||||
if inN < bufSz {
|
||||
buf = buf[:inN]
|
||||
}
|
||||
sb.WriteString(BytesToStr(buf))
|
||||
} else {
|
||||
if errors.Is(rErr, io.EOF) {
|
||||
rErr = nil
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if rErr != nil {
|
||||
err = rErr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package binarystr
|
||||
|
||||
import (
|
||||
`errors`
|
||||
`io`
|
||||
`strings`
|
||||
)
|
||||
|
||||
/*
|
||||
Read conforms to an [io.Reader].
|
||||
It returns bitstring encoding of the data in e's read source.
|
||||
|
||||
(To conform to [io.Reader], this is returned as bytes - e.g. []byte(string()).)
|
||||
|
||||
IMPORTANT: Read will return [ErrBadNumOct] if len(b) % 8 != 0! EIGHT BYTES in b equal ONE BYTE
|
||||
from e's read source! Keep this in mind and size b accordingly for your desired decoded data size!
|
||||
Likewise, n will ALWAYS be len(b), but the number of bytes actually read from e's internal reader
|
||||
will ALWAYS be n/8! (If for some bizarre case it isn't, err will be [BadReadErr].)
|
||||
*/
|
||||
func (e *Encoder) Read(b []byte) (n int, err error) {
|
||||
|
||||
var inN int
|
||||
var s string
|
||||
var buf []byte
|
||||
|
||||
defer func() {
|
||||
if b != nil && buf != nil {
|
||||
s = BytesToStr(buf)
|
||||
copy(b, []byte(s))
|
||||
}
|
||||
}()
|
||||
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
if len(b)%8 != 0 {
|
||||
err = ErrBadNumOct
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]byte, len(b)/8)
|
||||
if inN, err = e.r.Read(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if inN != len(buf) {
|
||||
if len(buf) > inN {
|
||||
buf = buf[:inN]
|
||||
}
|
||||
err = &BadReadErr{
|
||||
numBytes: uint64(inN),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ReadString returns a bitstring-encoded [string] from the entirety of the data
|
||||
in the source reader of e.
|
||||
|
||||
IMPORTANT: If e's source reader is a non-terminating stream (e.g. a [math/rand.Rand]),
|
||||
this function will NEVER RETURN (and will likely consume all memory).
|
||||
YOU HAVE BEEN WARNED.
|
||||
|
||||
n is the number of bytes read from e's source reader.
|
||||
*/
|
||||
func (e *Encoder) ReadString() (s string, n uint64, err error) {
|
||||
|
||||
var inN int
|
||||
var buf []byte
|
||||
var rErr error
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
defer func() {
|
||||
if sb != nil {
|
||||
s = sb.String()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
buf = make([]byte, bufSz)
|
||||
inN, rErr = e.r.Read(buf)
|
||||
n += uint64(inN)
|
||||
|
||||
if inN != 0 {
|
||||
if inN < bufSz {
|
||||
buf = buf[:inN]
|
||||
}
|
||||
sb.WriteString(BytesToStr(buf))
|
||||
} else {
|
||||
if errors.Is(rErr, io.EOF) {
|
||||
rErr = nil
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if rErr != nil {
|
||||
err = rErr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package binarystr
|
||||
|
||||
import (
|
||||
`io`
|
||||
)
|
||||
|
||||
type (
|
||||
Encoder struct {
|
||||
r io.Reader
|
||||
cur uint64
|
||||
}
|
||||
|
||||
Decoder struct {
|
||||
r io.Reader
|
||||
cur uint64
|
||||
}
|
||||
)
|
||||
Reference in new issue
Block a user