diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml index befb67e..7ae30fa 100644 --- a/.github/workflows/debug.yml +++ b/.github/workflows/debug.yml @@ -30,38 +30,6 @@ jobs: - name: Build run: | make test - build_go118: - name: Linux Debug build (Go 1.18) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v4 - with: - go-version: ~1.18 - continue-on-error: true - - name: Build - run: | - make test - build_go119: - name: Linux Debug build (Go 1.19) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v4 - with: - go-version: ~1.19 - continue-on-error: true - - name: Build - run: | - make test build_go120: name: Linux Debug build (Go 1.20) runs-on: ubuntu-latest diff --git a/common/binary/README.md b/common/binary/README.md new file mode 100644 index 0000000..4b82a6a --- /dev/null +++ b/common/binary/README.md @@ -0,0 +1,3 @@ +# binary + +mod from go 1.22.3 \ No newline at end of file diff --git a/common/binary/binary.go b/common/binary/binary.go new file mode 100644 index 0000000..41558ab --- /dev/null +++ b/common/binary/binary.go @@ -0,0 +1,817 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package binary implements simple translation between numbers and byte +// sequences and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (bool, int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// The varint functions encode and decode single integer values using +// a variable-length encoding; smaller values require fewer bytes. +// For a specification, see +// https://developers.google.com/protocol-buffers/docs/encoding. +// +// This package favors simplicity over efficiency. Clients that require +// high-performance serialization, especially for large data structures, +// should look at more advanced solutions such as the [encoding/gob] +// package or [google.golang.org/protobuf] for protocol buffers. +package binary + +import ( + "errors" + "io" + "math" + "reflect" + "sync" +) + +// A ByteOrder specifies how to convert byte slices into +// 16-, 32-, or 64-bit unsigned integers. +// +// It is implemented by [LittleEndian], [BigEndian], and [NativeEndian]. +type ByteOrder interface { + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 + PutUint16([]byte, uint16) + PutUint32([]byte, uint32) + PutUint64([]byte, uint64) + String() string +} + +// AppendByteOrder specifies how to append 16-, 32-, or 64-bit unsigned integers +// into a byte slice. +// +// It is implemented by [LittleEndian], [BigEndian], and [NativeEndian]. +type AppendByteOrder interface { + AppendUint16([]byte, uint16) []byte + AppendUint32([]byte, uint32) []byte + AppendUint64([]byte, uint64) []byte + String() string +} + +// LittleEndian is the little-endian implementation of [ByteOrder] and [AppendByteOrder]. +var LittleEndian littleEndian + +// BigEndian is the big-endian implementation of [ByteOrder] and [AppendByteOrder]. +var BigEndian bigEndian + +type littleEndian struct{} + +func (littleEndian) Uint16(b []byte) uint16 { + _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 + return uint16(b[0]) | uint16(b[1])<<8 +} + +func (littleEndian) PutUint16(b []byte, v uint16) { + _ = b[1] // early bounds check to guarantee safety of writes below + b[0] = byte(v) + b[1] = byte(v >> 8) +} + +func (littleEndian) AppendUint16(b []byte, v uint16) []byte { + return append(b, + byte(v), + byte(v>>8), + ) +} + +func (littleEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) PutUint32(b []byte, v uint32) { + _ = b[3] // early bounds check to guarantee safety of writes below + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func (littleEndian) AppendUint32(b []byte, v uint32) []byte { + return append(b, + byte(v), + byte(v>>8), + byte(v>>16), + byte(v>>24), + ) +} + +func (littleEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (littleEndian) PutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +func (littleEndian) AppendUint64(b []byte, v uint64) []byte { + return append(b, + byte(v), + byte(v>>8), + byte(v>>16), + byte(v>>24), + byte(v>>32), + byte(v>>40), + byte(v>>48), + byte(v>>56), + ) +} + +func (littleEndian) String() string { return "LittleEndian" } + +func (littleEndian) GoString() string { return "binary.LittleEndian" } + +type bigEndian struct{} + +func (bigEndian) Uint16(b []byte) uint16 { + _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 + return uint16(b[1]) | uint16(b[0])<<8 +} + +func (bigEndian) PutUint16(b []byte, v uint16) { + _ = b[1] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 8) + b[1] = byte(v) +} + +func (bigEndian) AppendUint16(b []byte, v uint16) []byte { + return append(b, + byte(v>>8), + byte(v), + ) +} + +func (bigEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) PutUint32(b []byte, v uint32) { + _ = b[3] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +func (bigEndian) AppendUint32(b []byte, v uint32) []byte { + return append(b, + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} + +func (bigEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func (bigEndian) PutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +func (bigEndian) AppendUint64(b []byte, v uint64) []byte { + return append(b, + byte(v>>56), + byte(v>>48), + byte(v>>40), + byte(v>>32), + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} + +func (bigEndian) String() string { return "BigEndian" } + +func (bigEndian) GoString() string { return "binary.BigEndian" } + +func (nativeEndian) String() string { return "NativeEndian" } + +func (nativeEndian) GoString() string { return "binary.NativeEndian" } + +// Read reads structured binary data from r into data. +// Data must be a pointer to a fixed-size value or a slice +// of fixed-size values. +// Bytes read from r are decoded using the specified byte order +// and written to successive fields of the data. +// When decoding boolean values, a zero byte is decoded as false, and +// any other non-zero byte is decoded as true. +// When reading into structs, the field data for fields with +// blank (_) field names is skipped; i.e., blank field names +// may be used for padding. +// When reading into a struct, all non-blank fields must be exported +// or Read may panic. +// +// The error is [io.EOF] only if no bytes were read. +// If an [io.EOF] happens after reading some but not all the bytes, +// Read returns [io.ErrUnexpectedEOF]. +func Read(r io.Reader, order ByteOrder, data any) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + bs := make([]byte, n) + if _, err := io.ReadFull(r, bs); err != nil { + return err + } + switch data := data.(type) { + case *bool: + *data = bs[0] != 0 + case *int8: + *data = int8(bs[0]) + case *uint8: + *data = bs[0] + case *int16: + *data = int16(order.Uint16(bs)) + case *uint16: + *data = order.Uint16(bs) + case *int32: + *data = int32(order.Uint32(bs)) + case *uint32: + *data = order.Uint32(bs) + case *int64: + *data = int64(order.Uint64(bs)) + case *uint64: + *data = order.Uint64(bs) + case *float32: + *data = math.Float32frombits(order.Uint32(bs)) + case *float64: + *data = math.Float64frombits(order.Uint64(bs)) + case []bool: + for i, x := range bs { // Easier to loop over the input for 8-bit values. + data[i] = x != 0 + } + case []int8: + for i, x := range bs { + data[i] = int8(x) + } + case []uint8: + copy(data, bs) + case []int16: + for i := range data { + data[i] = int16(order.Uint16(bs[2*i:])) + } + case []uint16: + for i := range data { + data[i] = order.Uint16(bs[2*i:]) + } + case []int32: + for i := range data { + data[i] = int32(order.Uint32(bs[4*i:])) + } + case []uint32: + for i := range data { + data[i] = order.Uint32(bs[4*i:]) + } + case []int64: + for i := range data { + data[i] = int64(order.Uint64(bs[8*i:])) + } + case []uint64: + for i := range data { + data[i] = order.Uint64(bs[8*i:]) + } + case []float32: + for i := range data { + data[i] = math.Float32frombits(order.Uint32(bs[4*i:])) + } + case []float64: + for i := range data { + data[i] = math.Float64frombits(order.Uint64(bs[8*i:])) + } + default: + n = 0 // fast path doesn't apply + } + if n != 0 { + return nil + } + } + + // Fallback to reflect-based decoding. + v := reflect.ValueOf(data) + size := -1 + switch v.Kind() { + case reflect.Pointer: + v = v.Elem() + size = dataSize(v) + case reflect.Slice: + size = dataSize(v) + } + if size < 0 { + return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String()) + } + d := &decoder{order: order, buf: make([]byte, size)} + if _, err := io.ReadFull(r, d.buf); err != nil { + return err + } + d.value(v) + return nil +} + +// Write writes the binary representation of data into w. +// Data must be a fixed-size value or a slice of fixed-size +// values, or a pointer to such data. +// Boolean values encode as one byte: 1 for true, and 0 for false. +// Bytes written to w are encoded using the specified byte order +// and read from successive fields of the data. +// When writing structs, zero values are written for fields +// with blank (_) field names. +func Write(w io.Writer, order ByteOrder, data any) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + bs := make([]byte, n) + switch v := data.(type) { + case *bool: + if *v { + bs[0] = 1 + } else { + bs[0] = 0 + } + case bool: + if v { + bs[0] = 1 + } else { + bs[0] = 0 + } + case []bool: + for i, x := range v { + if x { + bs[i] = 1 + } else { + bs[i] = 0 + } + } + case *int8: + bs[0] = byte(*v) + case int8: + bs[0] = byte(v) + case []int8: + for i, x := range v { + bs[i] = byte(x) + } + case *uint8: + bs[0] = *v + case uint8: + bs[0] = v + case []uint8: + bs = v + case *int16: + order.PutUint16(bs, uint16(*v)) + case int16: + order.PutUint16(bs, uint16(v)) + case []int16: + for i, x := range v { + order.PutUint16(bs[2*i:], uint16(x)) + } + case *uint16: + order.PutUint16(bs, *v) + case uint16: + order.PutUint16(bs, v) + case []uint16: + for i, x := range v { + order.PutUint16(bs[2*i:], x) + } + case *int32: + order.PutUint32(bs, uint32(*v)) + case int32: + order.PutUint32(bs, uint32(v)) + case []int32: + for i, x := range v { + order.PutUint32(bs[4*i:], uint32(x)) + } + case *uint32: + order.PutUint32(bs, *v) + case uint32: + order.PutUint32(bs, v) + case []uint32: + for i, x := range v { + order.PutUint32(bs[4*i:], x) + } + case *int64: + order.PutUint64(bs, uint64(*v)) + case int64: + order.PutUint64(bs, uint64(v)) + case []int64: + for i, x := range v { + order.PutUint64(bs[8*i:], uint64(x)) + } + case *uint64: + order.PutUint64(bs, *v) + case uint64: + order.PutUint64(bs, v) + case []uint64: + for i, x := range v { + order.PutUint64(bs[8*i:], x) + } + case *float32: + order.PutUint32(bs, math.Float32bits(*v)) + case float32: + order.PutUint32(bs, math.Float32bits(v)) + case []float32: + for i, x := range v { + order.PutUint32(bs[4*i:], math.Float32bits(x)) + } + case *float64: + order.PutUint64(bs, math.Float64bits(*v)) + case float64: + order.PutUint64(bs, math.Float64bits(v)) + case []float64: + for i, x := range v { + order.PutUint64(bs[8*i:], math.Float64bits(x)) + } + } + _, err := w.Write(bs) + return err + } + + // Fallback to reflect-based encoding. + v := reflect.Indirect(reflect.ValueOf(data)) + size := dataSize(v) + if size < 0 { + return errors.New("binary.Write: some values are not fixed-sized in type " + reflect.TypeOf(data).String()) + } + buf := make([]byte, size) + e := &encoder{order: order, buf: buf} + e.value(v) + _, err := w.Write(buf) + return err +} + +// Size returns how many bytes [Write] would generate to encode the value v, which +// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. +// If v is neither of these, Size returns -1. +func Size(v any) int { + return dataSize(reflect.Indirect(reflect.ValueOf(v))) +} + +var structSize sync.Map // map[reflect.Type]int + +// dataSize returns the number of bytes the actual data represented by v occupies in memory. +// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice +// it returns the length of the slice times the element size and does not count the memory +// occupied by the header. If the type of v is not acceptable, dataSize returns -1. +func dataSize(v reflect.Value) int { + switch v.Kind() { + case reflect.Slice: + if s := sizeof(v.Type().Elem()); s >= 0 { + return s * v.Len() + } + + case reflect.Struct: + t := v.Type() + if size, ok := structSize.Load(t); ok { + return size.(int) + } + size := sizeof(t) + structSize.Store(t, size) + return size + + default: + if v.IsValid() { + return sizeof(v.Type()) + } + } + + return -1 +} + +// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable. +func sizeof(t reflect.Type) int { + switch t.Kind() { + case reflect.Array: + if s := sizeof(t.Elem()); s >= 0 { + return s * t.Len() + } + + case reflect.Struct: + sum := 0 + for i, n := 0, t.NumField(); i < n; i++ { + s := sizeof(t.Field(i).Type) + if s < 0 { + return -1 + } + sum += s + } + return sum + + case reflect.Bool, + reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + return int(t.Size()) + } + + return -1 +} + +type coder struct { + order ByteOrder + buf []byte + offset int +} + +type ( + decoder coder + encoder coder +) + +func (d *decoder) bool() bool { + x := d.buf[d.offset] + d.offset++ + return x != 0 +} + +func (e *encoder) bool(x bool) { + if x { + e.buf[e.offset] = 1 + } else { + e.buf[e.offset] = 0 + } + e.offset++ +} + +func (d *decoder) uint8() uint8 { + x := d.buf[d.offset] + d.offset++ + return x +} + +func (e *encoder) uint8(x uint8) { + e.buf[e.offset] = x + e.offset++ +} + +func (d *decoder) uint16() uint16 { + x := d.order.Uint16(d.buf[d.offset : d.offset+2]) + d.offset += 2 + return x +} + +func (e *encoder) uint16(x uint16) { + e.order.PutUint16(e.buf[e.offset:e.offset+2], x) + e.offset += 2 +} + +func (d *decoder) uint32() uint32 { + x := d.order.Uint32(d.buf[d.offset : d.offset+4]) + d.offset += 4 + return x +} + +func (e *encoder) uint32(x uint32) { + e.order.PutUint32(e.buf[e.offset:e.offset+4], x) + e.offset += 4 +} + +func (d *decoder) uint64() uint64 { + x := d.order.Uint64(d.buf[d.offset : d.offset+8]) + d.offset += 8 + return x +} + +func (e *encoder) uint64(x uint64) { + e.order.PutUint64(e.buf[e.offset:e.offset+8], x) + e.offset += 8 +} + +func (d *decoder) int8() int8 { return int8(d.uint8()) } + +func (e *encoder) int8(x int8) { e.uint8(uint8(x)) } + +func (d *decoder) int16() int16 { return int16(d.uint16()) } + +func (e *encoder) int16(x int16) { e.uint16(uint16(x)) } + +func (d *decoder) int32() int32 { return int32(d.uint32()) } + +func (e *encoder) int32(x int32) { e.uint32(uint32(x)) } + +func (d *decoder) int64() int64 { return int64(d.uint64()) } + +func (e *encoder) int64(x int64) { e.uint64(uint64(x)) } + +func (d *decoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // Note: Calling v.CanSet() below is an optimization. + // It would be sufficient to check the field name, + // but creating the StructField info for each field is + // costly (run "go test -bench=ReadStruct" and compare + // results when making changes to this code). + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + d.value(v) + } else { + d.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Bool: + v.SetBool(d.bool()) + + case reflect.Int8: + v.SetInt(int64(d.int8())) + case reflect.Int16: + v.SetInt(int64(d.int16())) + case reflect.Int32: + v.SetInt(int64(d.int32())) + case reflect.Int64: + v.SetInt(d.int64()) + + case reflect.Uint8: + v.SetUint(uint64(d.uint8())) + case reflect.Uint16: + v.SetUint(uint64(d.uint16())) + case reflect.Uint32: + v.SetUint(uint64(d.uint32())) + case reflect.Uint64: + v.SetUint(d.uint64()) + + case reflect.Float32: + v.SetFloat(float64(math.Float32frombits(d.uint32()))) + case reflect.Float64: + v.SetFloat(math.Float64frombits(d.uint64())) + + case reflect.Complex64: + v.SetComplex(complex( + float64(math.Float32frombits(d.uint32())), + float64(math.Float32frombits(d.uint32())), + )) + case reflect.Complex128: + v.SetComplex(complex( + math.Float64frombits(d.uint64()), + math.Float64frombits(d.uint64()), + )) + } +} + +func (e *encoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // see comment for corresponding code in decoder.value() + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + e.value(v) + } else { + e.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Bool: + e.bool(v.Bool()) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type().Kind() { + case reflect.Int8: + e.int8(int8(v.Int())) + case reflect.Int16: + e.int16(int16(v.Int())) + case reflect.Int32: + e.int32(int32(v.Int())) + case reflect.Int64: + e.int64(v.Int()) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch v.Type().Kind() { + case reflect.Uint8: + e.uint8(uint8(v.Uint())) + case reflect.Uint16: + e.uint16(uint16(v.Uint())) + case reflect.Uint32: + e.uint32(uint32(v.Uint())) + case reflect.Uint64: + e.uint64(v.Uint()) + } + + case reflect.Float32, reflect.Float64: + switch v.Type().Kind() { + case reflect.Float32: + e.uint32(math.Float32bits(float32(v.Float()))) + case reflect.Float64: + e.uint64(math.Float64bits(v.Float())) + } + + case reflect.Complex64, reflect.Complex128: + switch v.Type().Kind() { + case reflect.Complex64: + x := v.Complex() + e.uint32(math.Float32bits(float32(real(x)))) + e.uint32(math.Float32bits(float32(imag(x)))) + case reflect.Complex128: + x := v.Complex() + e.uint64(math.Float64bits(real(x))) + e.uint64(math.Float64bits(imag(x))) + } + } +} + +func (d *decoder) skip(v reflect.Value) { + d.offset += dataSize(v) +} + +func (e *encoder) skip(v reflect.Value) { + n := dataSize(v) + zero := e.buf[e.offset : e.offset+n] + for i := range zero { + zero[i] = 0 + } + e.offset += n +} + +// intDataSize returns the size of the data required to represent the data when encoded. +// It returns zero if the type cannot be implemented by the fast path in Read or Write. +func intDataSize(data any) int { + switch data := data.(type) { + case bool, int8, uint8, *bool, *int8, *uint8: + return 1 + case []bool: + return len(data) + case []int8: + return len(data) + case []uint8: + return len(data) + case int16, uint16, *int16, *uint16: + return 2 + case []int16: + return 2 * len(data) + case []uint16: + return 2 * len(data) + case int32, uint32, *int32, *uint32: + return 4 + case []int32: + return 4 * len(data) + case []uint32: + return 4 * len(data) + case int64, uint64, *int64, *uint64: + return 8 + case []int64: + return 8 * len(data) + case []uint64: + return 8 * len(data) + case float32, *float32: + return 4 + case float64, *float64: + return 8 + case []float32: + return 4 * len(data) + case []float64: + return 8 * len(data) + } + return 0 +} diff --git a/common/binary/native_endian_big.go b/common/binary/native_endian_big.go new file mode 100644 index 0000000..bcc8e30 --- /dev/null +++ b/common/binary/native_endian_big.go @@ -0,0 +1,14 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 + +package binary + +type nativeEndian struct { + bigEndian +} + +// NativeEndian is the native-endian implementation of [ByteOrder] and [AppendByteOrder]. +var NativeEndian nativeEndian diff --git a/common/binary/native_endian_little.go b/common/binary/native_endian_little.go new file mode 100644 index 0000000..38d3e9b --- /dev/null +++ b/common/binary/native_endian_little.go @@ -0,0 +1,14 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh || wasm + +package binary + +type nativeEndian struct { + littleEndian +} + +// NativeEndian is the native-endian implementation of [ByteOrder] and [AppendByteOrder]. +var NativeEndian nativeEndian diff --git a/common/binary/variant_data.go b/common/binary/variant_data.go new file mode 100644 index 0000000..eb2b128 --- /dev/null +++ b/common/binary/variant_data.go @@ -0,0 +1,305 @@ +package binary + +import ( + "bufio" + "errors" + "io" + "reflect" + + E "github.com/sagernet/sing/common/exceptions" +) + +func ReadDataSlice(r *bufio.Reader, order ByteOrder, data ...any) error { + for index, item := range data { + err := ReadData(r, order, item) + if err != nil { + return E.Cause(err, "[", index, "]") + } + } + return nil +} + +func ReadData(r *bufio.Reader, order ByteOrder, data any) error { + switch dataPtr := data.(type) { + case *[]uint8: + bytesLen, err := ReadUvarint(r) + if err != nil { + return E.Cause(err, "bytes length") + } + newBytes := make([]uint8, bytesLen) + _, err = io.ReadFull(r, newBytes) + if err != nil { + return E.Cause(err, "bytes value") + } + *dataPtr = newBytes + default: + if intBaseDataSize(data) != 0 { + return Read(r, order, data) + } + } + dataValue := reflect.ValueOf(data) + if dataValue.Kind() == reflect.Pointer { + dataValue = dataValue.Elem() + } + return readData(r, order, dataValue) +} + +func readData(r *bufio.Reader, order ByteOrder, data reflect.Value) error { + switch data.Kind() { + case reflect.Pointer: + pointerValue, err := r.ReadByte() + if err != nil { + return err + } + if pointerValue == 0 { + data.SetZero() + return nil + } + if data.IsNil() { + data.Set(reflect.New(data.Type().Elem())) + } + return readData(r, order, data.Elem()) + case reflect.String: + stringLength, err := ReadUvarint(r) + if err != nil { + return E.Cause(err, "string length") + } + if stringLength == 0 { + data.SetZero() + } else { + stringData := make([]byte, stringLength) + _, err = io.ReadFull(r, stringData) + if err != nil { + return E.Cause(err, "string value") + } + data.SetString(string(stringData)) + } + case reflect.Array: + arrayLen := data.Len() + for i := 0; i < arrayLen; i++ { + err := readData(r, order, data.Index(i)) + if err != nil { + return E.Cause(err, "[", i, "]") + } + } + case reflect.Slice: + sliceLength, err := ReadUvarint(r) + if err != nil { + return E.Cause(err, "slice length") + } + if !data.IsNil() && data.Cap() >= int(sliceLength) { + data.SetLen(int(sliceLength)) + } else if sliceLength > 0 { + data.Set(reflect.MakeSlice(data.Type(), int(sliceLength), int(sliceLength))) + } + if sliceLength > 0 { + if data.Type().Elem().Kind() == reflect.Uint8 { + _, err = io.ReadFull(r, data.Bytes()) + if err != nil { + return E.Cause(err, "bytes value") + } + } else { + for index := 0; index < int(sliceLength); index++ { + err = readData(r, order, data.Index(index)) + if err != nil { + return E.Cause(err, "[", index, "]") + } + } + } + } + case reflect.Map: + mapLength, err := ReadUvarint(r) + if err != nil { + return E.Cause(err, "map length") + } + data.Set(reflect.MakeMap(data.Type())) + for index := 0; index < int(mapLength); index++ { + key := reflect.New(data.Type().Key()).Elem() + err = readData(r, order, key) + if err != nil { + return E.Cause(err, "[", index, "].key") + } + value := reflect.New(data.Type().Elem()).Elem() + err = readData(r, order, value) + if err != nil { + return E.Cause(err, "[", index, "].value") + } + data.SetMapIndex(key, value) + } + case reflect.Struct: + fieldType := data.Type() + fieldLen := data.NumField() + for i := 0; i < fieldLen; i++ { + field := data.Field(i) + fieldName := fieldType.Field(i).Name + if field.CanSet() || fieldName != "_" { + err := readData(r, order, field) + if err != nil { + return E.Cause(err, fieldName) + } + } + } + default: + size := dataSize(data) + if size < 0 { + return errors.New("invalid type " + reflect.TypeOf(data).String()) + } + d := &decoder{order: order, buf: make([]byte, size)} + _, err := io.ReadFull(r, d.buf) + if err != nil { + return err + } + d.value(data) + } + return nil +} + +func WriteDataSlice(writer *bufio.Writer, order ByteOrder, data ...any) error { + for index, item := range data { + err := WriteData(writer, order, item) + if err != nil { + return E.Cause(err, "[", index, "]") + } + } + return nil +} + +func WriteData(writer *bufio.Writer, order ByteOrder, data any) error { + switch dataPtr := data.(type) { + case []uint8: + _, err := writer.Write(AppendUvarint(writer.AvailableBuffer(), uint64(len(dataPtr)))) + if err != nil { + return E.Cause(err, "bytes length") + } + _, err = writer.Write(dataPtr) + if err != nil { + return E.Cause(err, "bytes value") + } + default: + if intBaseDataSize(data) != 0 { + return Write(writer, order, data) + } + } + return writeData(writer, order, reflect.Indirect(reflect.ValueOf(data))) +} + +func writeData(writer *bufio.Writer, order ByteOrder, data reflect.Value) error { + switch data.Kind() { + case reflect.Pointer: + if data.IsNil() { + err := writer.WriteByte(0) + if err != nil { + return err + } + } else { + err := writer.WriteByte(1) + if err != nil { + return err + } + return writeData(writer, order, data.Elem()) + } + case reflect.String: + stringValue := data.String() + _, err := writer.Write(AppendUvarint(writer.AvailableBuffer(), uint64(len(stringValue)))) + if err != nil { + return E.Cause(err, "string length") + } + if stringValue != "" { + _, err = writer.WriteString(stringValue) + if err != nil { + return E.Cause(err, "string value") + } + } + case reflect.Array: + dataLen := data.Len() + for i := 0; i < dataLen; i++ { + err := writeData(writer, order, data.Index(i)) + if err != nil { + return E.Cause(err, "[", i, "]") + } + } + case reflect.Slice: + dataLen := data.Len() + _, err := writer.Write(AppendUvarint(writer.AvailableBuffer(), uint64(dataLen))) + if err != nil { + return E.Cause(err, "slice length") + } + if dataLen > 0 { + if data.Type().Elem().Kind() == reflect.Uint8 { + _, err = writer.Write(data.Bytes()) + if err != nil { + return E.Cause(err, "bytes value") + } + } else { + for i := 0; i < dataLen; i++ { + err = writeData(writer, order, data.Index(i)) + if err != nil { + return E.Cause(err, "[", i, "]") + } + } + } + } + case reflect.Map: + dataLen := data.Len() + _, err := writer.Write(AppendUvarint(writer.AvailableBuffer(), uint64(dataLen))) + if err != nil { + return E.Cause(err, "map length") + } + if dataLen > 0 { + for index, key := range data.MapKeys() { + err = writeData(writer, order, key) + if err != nil { + return E.Cause(err, "[", index, "].key") + } + err = writeData(writer, order, data.MapIndex(key)) + if err != nil { + return E.Cause(err, "[", index, "].value") + } + } + } + case reflect.Struct: + fieldType := data.Type() + fieldLen := data.NumField() + for i := 0; i < fieldLen; i++ { + field := data.Field(i) + fieldName := fieldType.Field(i).Name + if field.CanSet() || fieldName != "_" { + err := writeData(writer, order, field) + if err != nil { + return E.Cause(err, fieldName) + } + } + } + default: + size := dataSize(data) + if size < 0 { + return errors.New("binary.Write: some values are not fixed-sized in type " + data.Type().String()) + } + buf := make([]byte, size) + e := &encoder{order: order, buf: buf} + e.value(data) + _, err := writer.Write(buf) + if err != nil { + return E.Cause(err, reflect.TypeOf(data).String()) + } + } + return nil +} + +func intBaseDataSize(data any) int { + switch data.(type) { + case bool, int8, uint8: + return 1 + case int16, uint16: + return 2 + case int32, uint32: + return 4 + case int64, uint64: + return 8 + case float32: + return 4 + case float64: + return 8 + } + return 0 +} diff --git a/common/binary/varint.go b/common/binary/varint.go new file mode 100644 index 0000000..64dd9d6 --- /dev/null +++ b/common/binary/varint.go @@ -0,0 +1,166 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package binary + +// This file implements "varint" encoding of 64-bit integers. +// The encoding is: +// - unsigned integers are serialized 7 bits at a time, starting with the +// least significant bits +// - the most significant bit (msb) in each output byte indicates if there +// is a continuation byte (msb = 1) +// - signed integers are mapped to unsigned integers using "zig-zag" +// encoding: Positive values x are written as 2*x + 0, negative values +// are written as 2*(^x) + 1; that is, negative numbers are complemented +// and whether to complement is encoded in bit 0. +// +// Design note: +// At most 10 bytes are needed for 64-bit values. The encoding could +// be more dense: a full 64-bit value needs an extra byte just to hold bit 63. +// Instead, the msb of the previous byte could be used to hold bit 63 since we +// know there can't be more than 64 bits. This is a trivial improvement and +// would reduce the maximum encoding length to 9 bytes. However, it breaks the +// invariant that the msb is always the "continuation bit" and thus makes the +// format incompatible with a varint encoding for larger numbers (say 128-bit). + +import ( + "errors" + "io" +) + +// MaxVarintLenN is the maximum length of a varint-encoded N-bit integer. +const ( + MaxVarintLen16 = 3 + MaxVarintLen32 = 5 + MaxVarintLen64 = 10 +) + +// AppendUvarint appends the varint-encoded form of x, +// as generated by [PutUvarint], to buf and returns the extended buffer. +func AppendUvarint(buf []byte, x uint64) []byte { + for x >= 0x80 { + buf = append(buf, byte(x)|0x80) + x >>= 7 + } + return append(buf, byte(x)) +} + +// PutUvarint encodes a uint64 into buf and returns the number of bytes written. +// If the buffer is too small, PutUvarint will panic. +func PutUvarint(buf []byte, x uint64) int { + i := 0 + for x >= 0x80 { + buf[i] = byte(x) | 0x80 + x >>= 7 + i++ + } + buf[i] = byte(x) + return i + 1 +} + +// Uvarint decodes a uint64 from buf and returns that value and the +// number of bytes read (> 0). If an error occurred, the value is 0 +// and the number of bytes n is <= 0 meaning: +// +// n == 0: buf too small +// n < 0: value larger than 64 bits (overflow) +// and -n is the number of bytes read +func Uvarint(buf []byte) (uint64, int) { + var x uint64 + var s uint + for i, b := range buf { + if i == MaxVarintLen64 { + // Catch byte reads past MaxVarintLen64. + // See issue https://golang.org/issues/41185 + return 0, -(i + 1) // overflow + } + if b < 0x80 { + if i == MaxVarintLen64-1 && b > 1 { + return 0, -(i + 1) // overflow + } + return x | uint64(b)< 0). If an error occurred, the value is 0 +// and the number of bytes n is <= 0 with the following meaning: +// +// n == 0: buf too small +// n < 0: value larger than 64 bits (overflow) +// and -n is the number of bytes read +func Varint(buf []byte) (int64, int) { + ux, n := Uvarint(buf) // ok to continue in presence of error + x := int64(ux >> 1) + if ux&1 != 0 { + x = ^x + } + return x, n +} + +var errOverflow = errors.New("binary: varint overflows a 64-bit integer") + +// ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64. +// The error is [io.EOF] only if no bytes were read. +// If an [io.EOF] happens after reading some but not all the bytes, +// ReadUvarint returns [io.ErrUnexpectedEOF]. +func ReadUvarint(r io.ByteReader) (uint64, error) { + var x uint64 + var s uint + for i := 0; i < MaxVarintLen64; i++ { + b, err := r.ReadByte() + if err != nil { + if i > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return x, err + } + if b < 0x80 { + if i == MaxVarintLen64-1 && b > 1 { + return x, errOverflow + } + return x | uint64(b)<> 1) + if ux&1 != 0 { + x = ^x + } + return x, err +} diff --git a/common/control/bind_finder.go b/common/control/bind_finder.go index 47e6a8e..2383217 100644 --- a/common/control/bind_finder.go +++ b/common/control/bind_finder.go @@ -1,18 +1,21 @@ package control import ( + "net" "net/netip" ) type InterfaceFinder interface { + Interfaces() []Interface InterfaceIndexByName(name string) (int, error) InterfaceNameByIndex(index int) (string, error) InterfaceByAddr(addr netip.Addr) (*Interface, error) } type Interface struct { - Index int - MTU int - Name string - Addresses []netip.Prefix + Index int + MTU int + Name string + Addresses []netip.Prefix + HardwareAddr net.HardwareAddr } diff --git a/common/control/bind_finder_default.go b/common/control/bind_finder_default.go index d8c2eed..9d9230e 100644 --- a/common/control/bind_finder_default.go +++ b/common/control/bind_finder_default.go @@ -9,6 +9,8 @@ import ( M "github.com/sagernet/sing/common/metadata" ) +var _ InterfaceFinder = (*DefaultInterfaceFinder)(nil) + type DefaultInterfaceFinder struct { interfaces []Interface } @@ -29,10 +31,11 @@ func (f *DefaultInterfaceFinder) Update() error { return err } interfaces = append(interfaces, Interface{ - Index: netIf.Index, - MTU: netIf.MTU, - Name: netIf.Name, - Addresses: common.Map(ifAddrs, M.PrefixFromNet), + Index: netIf.Index, + MTU: netIf.MTU, + Name: netIf.Name, + Addresses: common.Map(ifAddrs, M.PrefixFromNet), + HardwareAddr: netIf.HardwareAddr, }) } f.interfaces = interfaces @@ -43,6 +46,10 @@ func (f *DefaultInterfaceFinder) UpdateInterfaces(interfaces []Interface) { f.interfaces = interfaces } +func (f *DefaultInterfaceFinder) Interfaces() []Interface { + return f.interfaces +} + func (f *DefaultInterfaceFinder) InterfaceIndexByName(name string) (int, error) { for _, netInterface := range f.interfaces { if netInterface.Name == name { diff --git a/common/control/interface.go b/common/control/interface.go index f778a4b..01f07b4 100644 --- a/common/control/interface.go +++ b/common/control/interface.go @@ -3,6 +3,7 @@ package control import ( "syscall" + "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" ) @@ -30,6 +31,14 @@ func Conn(conn syscall.Conn, block func(fd uintptr) error) error { return Raw(rawConn, block) } +func Conn0[T any](conn syscall.Conn, block func(fd uintptr) (T, error)) (T, error) { + rawConn, err := conn.SyscallConn() + if err != nil { + return common.DefaultValue[T](), err + } + return Raw0[T](rawConn, block) +} + func Raw(rawConn syscall.RawConn, block func(fd uintptr) error) error { var innerErr error err := rawConn.Control(func(fd uintptr) { @@ -37,3 +46,14 @@ func Raw(rawConn syscall.RawConn, block func(fd uintptr) error) error { }) return E.Errors(innerErr, err) } + +func Raw0[T any](rawConn syscall.RawConn, block func(fd uintptr) (T, error)) (T, error) { + var ( + value T + innerErr error + ) + err := rawConn.Control(func(fd uintptr) { + value, innerErr = block(fd) + }) + return value, E.Errors(innerErr, err) +} diff --git a/common/control/redirect_darwin.go b/common/control/redirect_darwin.go new file mode 100644 index 0000000..50db3d8 --- /dev/null +++ b/common/control/redirect_darwin.go @@ -0,0 +1,58 @@ +package control + +import ( + "encoding/binary" + "net" + "net/netip" + "syscall" + "unsafe" + + M "github.com/sagernet/sing/common/metadata" + + "golang.org/x/sys/unix" +) + +const ( + PF_OUT = 0x2 + DIOCNATLOOK = 0xc0544417 +) + +func GetOriginalDestination(conn net.Conn) (netip.AddrPort, error) { + pfFd, err := syscall.Open("/dev/pf", 0, syscall.O_RDONLY) + if err != nil { + return netip.AddrPort{}, err + } + defer syscall.Close(pfFd) + nl := struct { + saddr, daddr, rsaddr, rdaddr [16]byte + sxport, dxport, rsxport, rdxport [4]byte + af, proto, protoVariant, direction uint8 + }{ + af: syscall.AF_INET, + proto: syscall.IPPROTO_TCP, + direction: PF_OUT, + } + localAddr := M.SocksaddrFromNet(conn.LocalAddr()).Unwrap() + removeAddr := M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap() + if localAddr.IsIPv4() { + copy(nl.saddr[:net.IPv4len], removeAddr.Addr.AsSlice()) + copy(nl.daddr[:net.IPv4len], localAddr.Addr.AsSlice()) + nl.af = syscall.AF_INET + } else { + copy(nl.saddr[:], removeAddr.Addr.AsSlice()) + copy(nl.daddr[:], localAddr.Addr.AsSlice()) + nl.af = syscall.AF_INET6 + } + binary.BigEndian.PutUint16(nl.sxport[:], removeAddr.Port) + binary.BigEndian.PutUint16(nl.dxport[:], localAddr.Port) + if _, _, errno := unix.Syscall(syscall.SYS_IOCTL, uintptr(pfFd), DIOCNATLOOK, uintptr(unsafe.Pointer(&nl))); errno != 0 { + return netip.AddrPort{}, errno + } + var address netip.Addr + if nl.af == unix.AF_INET { + address = M.AddrFromIP(nl.rdaddr[:net.IPv4len]) + } else { + address = netip.AddrFrom16(nl.rdaddr) + } + return netip.AddrPortFrom(address, binary.BigEndian.Uint16(nl.rdxport[:])), nil +} diff --git a/common/control/redirect_linux.go b/common/control/redirect_linux.go new file mode 100644 index 0000000..82ab233 --- /dev/null +++ b/common/control/redirect_linux.go @@ -0,0 +1,38 @@ +package control + +import ( + "encoding/binary" + "net" + "net/netip" + "os" + "syscall" + + "github.com/sagernet/sing/common" + M "github.com/sagernet/sing/common/metadata" + + "golang.org/x/sys/unix" +) + +func GetOriginalDestination(conn net.Conn) (netip.AddrPort, error) { + syscallConn, loaded := common.Cast[syscall.Conn](conn) + if !loaded { + return netip.AddrPort{}, os.ErrInvalid + } + return Conn0[netip.AddrPort](syscallConn, func(fd uintptr) (netip.AddrPort, error) { + if M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap().IsIPv4() { + raw, err := unix.GetsockoptIPv6Mreq(int(fd), unix.IPPROTO_IP, unix.SO_ORIGINAL_DST) + if err != nil { + return netip.AddrPort{}, err + } + return netip.AddrPortFrom(M.AddrFromIP(raw.Multiaddr[4:8]), uint16(raw.Multiaddr[2])<<8+uint16(raw.Multiaddr[3])), nil + } else { + raw, err := unix.GetsockoptIPv6MTUInfo(int(fd), unix.IPPROTO_IPV6, unix.SO_ORIGINAL_DST) + if err != nil { + return netip.AddrPort{}, err + } + var port [2]byte + binary.BigEndian.PutUint16(port[:], raw.Addr.Port) + return netip.AddrPortFrom(M.AddrFromIP(raw.Addr.Addr[:]), binary.LittleEndian.Uint16(port[:])), nil + } + }) +} diff --git a/common/control/redirect_other.go b/common/control/redirect_other.go new file mode 100644 index 0000000..b0f3297 --- /dev/null +++ b/common/control/redirect_other.go @@ -0,0 +1,13 @@ +//go:build !linux && !darwin + +package control + +import ( + "net" + "net/netip" + "os" +) + +func GetOriginalDestination(conn net.Conn) (netip.AddrPort, error) { + return netip.AddrPort{}, os.ErrInvalid +} diff --git a/common/control/tproxy_linux.go b/common/control/tproxy_linux.go new file mode 100644 index 0000000..b296b98 --- /dev/null +++ b/common/control/tproxy_linux.go @@ -0,0 +1,56 @@ +package control + +import ( + "encoding/binary" + "net/netip" + "syscall" + + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + + "golang.org/x/sys/unix" +) + +func TProxy(fd uintptr, family int) error { + err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + if err == nil { + err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1) + } + if err == nil && family == unix.AF_INET6 { + err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1) + } + if err == nil { + err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1) + } + if err == nil && family == unix.AF_INET6 { + err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1) + } + return err +} + +func TProxyWriteBack() Func { + return func(network, address string, conn syscall.RawConn) error { + return Raw(conn, func(fd uintptr) error { + if M.ParseSocksaddr(address).Addr.Is6() { + return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1) + } else { + return syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1) + } + }) + } +} + +func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) { + controlMessages, err := unix.ParseSocketControlMessage(oob) + if err != nil { + return netip.AddrPort{}, err + } + for _, message := range controlMessages { + if message.Header.Level == unix.SOL_IP && message.Header.Type == unix.IP_RECVORIGDSTADDR { + return netip.AddrPortFrom(M.AddrFromIP(message.Data[4:8]), binary.BigEndian.Uint16(message.Data[2:4])), nil + } else if message.Header.Level == unix.SOL_IPV6 && message.Header.Type == unix.IPV6_RECVORIGDSTADDR { + return netip.AddrPortFrom(M.AddrFromIP(message.Data[8:24]), binary.BigEndian.Uint16(message.Data[2:4])), nil + } + } + return netip.AddrPort{}, E.New("not found") +} diff --git a/common/control/tproxy_other.go b/common/control/tproxy_other.go new file mode 100644 index 0000000..cad1808 --- /dev/null +++ b/common/control/tproxy_other.go @@ -0,0 +1,20 @@ +//go:build !linux + +package control + +import ( + "net/netip" + "os" +) + +func TProxy(fd uintptr, isIPv6 bool) error { + return os.ErrInvalid +} + +func TProxyWriteBack() Func { + return nil +} + +func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) { + return netip.AddrPort{}, os.ErrInvalid +} diff --git a/common/json/context.go b/common/json/context.go index b68d571..7a49070 100644 --- a/common/json/context.go +++ b/common/json/context.go @@ -1,4 +1,4 @@ -//go:build go1.21 && !without_contextjson +//go:build go1.20 && !without_contextjson package json diff --git a/common/json/context_120.go b/common/json/context_120.go deleted file mode 100644 index c31e6ea..0000000 --- a/common/json/context_120.go +++ /dev/null @@ -1,23 +0,0 @@ -//go:build !go1.21 && go1.20 && !without_contextjson - -package json - -import ( - "github.com/sagernet/sing/common/json/internal/contextjson_120" -) - -var ( - Marshal = json.Marshal - Unmarshal = json.Unmarshal - NewEncoder = json.NewEncoder - NewDecoder = json.NewDecoder -) - -type ( - Encoder = json.Encoder - Decoder = json.Decoder - Token = json.Token - Delim = json.Delim - SyntaxError = json.SyntaxError - RawMessage = json.RawMessage -) diff --git a/common/json/internal/contextjson/encode.go b/common/json/internal/contextjson/encode.go index 6da0bd9..296177a 100644 --- a/common/json/internal/contextjson/encode.go +++ b/common/json/internal/contextjson/encode.go @@ -442,7 +442,7 @@ func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { b, err := m.MarshalJSON() if err == nil { e.Grow(len(b)) - out := e.AvailableBuffer() + out := availableBuffer(&e.Buffer) out, err = appendCompact(out, b, opts.escapeHTML) e.Buffer.Write(out) } @@ -461,7 +461,7 @@ func addrMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { b, err := m.MarshalJSON() if err == nil { e.Grow(len(b)) - out := e.AvailableBuffer() + out := availableBuffer(&e.Buffer) out, err = appendCompact(out, b, opts.escapeHTML) e.Buffer.Write(out) } @@ -484,7 +484,7 @@ func textMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { if err != nil { e.error(&MarshalerError{v.Type(), err, "MarshalText"}) } - e.Write(appendString(e.AvailableBuffer(), b, opts.escapeHTML)) + e.Write(appendString(availableBuffer(&e.Buffer), b, opts.escapeHTML)) } func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { @@ -498,11 +498,11 @@ func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { if err != nil { e.error(&MarshalerError{v.Type(), err, "MarshalText"}) } - e.Write(appendString(e.AvailableBuffer(), b, opts.escapeHTML)) + e.Write(appendString(availableBuffer(&e.Buffer), b, opts.escapeHTML)) } func boolEncoder(e *encodeState, v reflect.Value, opts encOpts) { - b := e.AvailableBuffer() + b := availableBuffer(&e.Buffer) b = mayAppendQuote(b, opts.quoted) b = strconv.AppendBool(b, v.Bool()) b = mayAppendQuote(b, opts.quoted) @@ -510,7 +510,7 @@ func boolEncoder(e *encodeState, v reflect.Value, opts encOpts) { } func intEncoder(e *encodeState, v reflect.Value, opts encOpts) { - b := e.AvailableBuffer() + b := availableBuffer(&e.Buffer) b = mayAppendQuote(b, opts.quoted) b = strconv.AppendInt(b, v.Int(), 10) b = mayAppendQuote(b, opts.quoted) @@ -518,7 +518,7 @@ func intEncoder(e *encodeState, v reflect.Value, opts encOpts) { } func uintEncoder(e *encodeState, v reflect.Value, opts encOpts) { - b := e.AvailableBuffer() + b := availableBuffer(&e.Buffer) b = mayAppendQuote(b, opts.quoted) b = strconv.AppendUint(b, v.Uint(), 10) b = mayAppendQuote(b, opts.quoted) @@ -538,7 +538,7 @@ func (bits floatEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { // See golang.org/issue/6384 and golang.org/issue/14135. // Like fmt %g, but the exponent cutoffs are different // and exponents themselves are not padded to two digits. - b := e.AvailableBuffer() + b := availableBuffer(&e.Buffer) b = mayAppendQuote(b, opts.quoted) abs := math.Abs(f) fmt := byte('f') @@ -577,7 +577,7 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) { if !isValidNumber(numStr) { e.error(fmt.Errorf("json: invalid number literal %q", numStr)) } - b := e.AvailableBuffer() + b := availableBuffer(&e.Buffer) b = mayAppendQuote(b, opts.quoted) b = append(b, numStr...) b = mayAppendQuote(b, opts.quoted) @@ -586,9 +586,9 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) { } if opts.quoted { b := appendString(nil, v.String(), opts.escapeHTML) - e.Write(appendString(e.AvailableBuffer(), b, false)) // no need to escape again since it is already escaped + e.Write(appendString(availableBuffer(&e.Buffer), b, false)) // no need to escape again since it is already escaped } else { - e.Write(appendString(e.AvailableBuffer(), v.String(), opts.escapeHTML)) + e.Write(appendString(availableBuffer(&e.Buffer), v.String(), opts.escapeHTML)) } } @@ -754,7 +754,7 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if i > 0 { e.WriteByte(',') } - e.Write(appendString(e.AvailableBuffer(), kv.ks, opts.escapeHTML)) + e.Write(appendString(availableBuffer(&e.Buffer), kv.ks, opts.escapeHTML)) e.WriteByte(':') me.elemEnc(e, kv.v, opts) } @@ -786,7 +786,7 @@ func encodeByteSlice(e *encodeState, v reflect.Value, _ encOpts) { e.Grow(len(`"`) + encodedLen + len(`"`)) // TODO(https://go.dev/issue/53693): Use base64.Encoding.AppendEncode. - b := e.AvailableBuffer() + b := availableBuffer(&e.Buffer) b = append(b, '"') base64.StdEncoding.Encode(b[len(b):][:encodedLen], s) b = b[:len(b)+encodedLen] diff --git a/common/json/internal/contextjson/indent.go b/common/json/internal/contextjson/indent.go index 26bb5d2..9995120 100644 --- a/common/json/internal/contextjson/indent.go +++ b/common/json/internal/contextjson/indent.go @@ -6,6 +6,11 @@ package json import "bytes" +// TODO(https://go.dev/issue/53685): Use bytes.Buffer.AvailableBuffer instead. +func availableBuffer(b *bytes.Buffer) []byte { + return b.Bytes()[b.Len():] +} + // HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 // characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 // so that the JSON will be safe to embed inside HTML