mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
This commit is contained in:
100
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/code.go
generated
vendored
Normal file
100
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/code.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func decodeCode(r *bytes.Reader, codeSectionStart uint64, ret *wasm.Code) (err error) {
|
||||
ss, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get the size of code: %w", err)
|
||||
}
|
||||
remaining := int64(ss)
|
||||
|
||||
// Parse #locals.
|
||||
ls, bytesRead, err := leb128.DecodeUint32(r)
|
||||
remaining -= int64(bytesRead)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get the size locals: %v", err)
|
||||
} else if remaining < 0 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
// Validate the locals.
|
||||
bytesRead = 0
|
||||
var sum uint64
|
||||
for i := uint32(0); i < ls; i++ {
|
||||
num, n, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read n of locals: %v", err)
|
||||
} else if remaining < 0 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
sum += uint64(num)
|
||||
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read type of local: %v", err)
|
||||
}
|
||||
|
||||
bytesRead += n + 1
|
||||
switch vt := b; vt {
|
||||
case wasm.ValueTypeI32, wasm.ValueTypeF32, wasm.ValueTypeI64, wasm.ValueTypeF64,
|
||||
wasm.ValueTypeFuncref, wasm.ValueTypeExternref, wasm.ValueTypeV128:
|
||||
default:
|
||||
return fmt.Errorf("invalid local type: 0x%x", vt)
|
||||
}
|
||||
}
|
||||
|
||||
if sum > math.MaxUint32 {
|
||||
return fmt.Errorf("too many locals: %d", sum)
|
||||
}
|
||||
|
||||
// Rewind the buffer.
|
||||
_, err = r.Seek(-int64(bytesRead), io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localTypes := make([]wasm.ValueType, 0, sum)
|
||||
for i := uint32(0); i < ls; i++ {
|
||||
num, bytesRead, err := leb128.DecodeUint32(r)
|
||||
remaining -= int64(bytesRead) + 1 // +1 for the subsequent ReadByte
|
||||
if err != nil {
|
||||
return fmt.Errorf("read n of locals: %v", err)
|
||||
} else if remaining < 0 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read type of local: %v", err)
|
||||
}
|
||||
|
||||
for j := uint32(0); j < num; j++ {
|
||||
localTypes = append(localTypes, b)
|
||||
}
|
||||
}
|
||||
|
||||
bodyOffsetInCodeSection := codeSectionStart - uint64(r.Len())
|
||||
body := make([]byte, remaining)
|
||||
if _, err = io.ReadFull(r, body); err != nil {
|
||||
return fmt.Errorf("read body: %w", err)
|
||||
}
|
||||
|
||||
if endIndex := len(body) - 1; endIndex < 0 || body[endIndex] != wasm.OpcodeEnd {
|
||||
return fmt.Errorf("expr not end with OpcodeEnd")
|
||||
}
|
||||
|
||||
ret.BodyOffsetInCodeSection = bodyOffsetInCodeSection
|
||||
ret.LocalTypes = localTypes
|
||||
ret.Body = body
|
||||
return nil
|
||||
}
|
105
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/const_expr.go
generated
vendored
Normal file
105
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/const_expr.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/ieee754"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func decodeConstantExpression(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.ConstantExpression) error {
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read opcode: %v", err)
|
||||
}
|
||||
|
||||
remainingBeforeData := int64(r.Len())
|
||||
offsetAtData := r.Size() - remainingBeforeData
|
||||
|
||||
opcode := b
|
||||
switch opcode {
|
||||
case wasm.OpcodeI32Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
_, _, err = leb128.DecodeInt32(r)
|
||||
case wasm.OpcodeI64Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
_, _, err = leb128.DecodeInt64(r)
|
||||
case wasm.OpcodeF32Const:
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return fmt.Errorf("read f32 constant: %v", err)
|
||||
}
|
||||
_, err = ieee754.DecodeFloat32(buf)
|
||||
case wasm.OpcodeF64Const:
|
||||
buf := make([]byte, 8)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return fmt.Errorf("read f64 constant: %v", err)
|
||||
}
|
||||
_, err = ieee754.DecodeFloat64(buf)
|
||||
case wasm.OpcodeGlobalGet:
|
||||
_, _, err = leb128.DecodeUint32(r)
|
||||
case wasm.OpcodeRefNull:
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
|
||||
return fmt.Errorf("ref.null is not supported as %w", err)
|
||||
}
|
||||
reftype, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read reference type for ref.null: %w", err)
|
||||
} else if reftype != wasm.RefTypeFuncref && reftype != wasm.RefTypeExternref {
|
||||
return fmt.Errorf("invalid type for ref.null: 0x%x", reftype)
|
||||
}
|
||||
case wasm.OpcodeRefFunc:
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
|
||||
return fmt.Errorf("ref.func is not supported as %w", err)
|
||||
}
|
||||
// Parsing index.
|
||||
_, _, err = leb128.DecodeUint32(r)
|
||||
case wasm.OpcodeVecPrefix:
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureSIMD); err != nil {
|
||||
return fmt.Errorf("vector instructions are not supported as %w", err)
|
||||
}
|
||||
opcode, err = r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read vector instruction opcode suffix: %w", err)
|
||||
}
|
||||
|
||||
if opcode != wasm.OpcodeVecV128Const {
|
||||
return fmt.Errorf("invalid vector opcode for const expression: %#x", opcode)
|
||||
}
|
||||
|
||||
remainingBeforeData = int64(r.Len())
|
||||
offsetAtData = r.Size() - remainingBeforeData
|
||||
|
||||
n, err := r.Read(make([]byte, 16))
|
||||
if err != nil {
|
||||
return fmt.Errorf("read vector const instruction immediates: %w", err)
|
||||
} else if n != 16 {
|
||||
return fmt.Errorf("read vector const instruction immediates: needs 16 bytes but was %d bytes", n)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%v for const expression opt code: %#x", ErrInvalidByte, b)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("read value: %v", err)
|
||||
}
|
||||
|
||||
if b, err = r.ReadByte(); err != nil {
|
||||
return fmt.Errorf("look for end opcode: %v", err)
|
||||
}
|
||||
|
||||
if b != wasm.OpcodeEnd {
|
||||
return fmt.Errorf("constant expression has been not terminated")
|
||||
}
|
||||
|
||||
ret.Data = make([]byte, remainingBeforeData-int64(r.Len())-1)
|
||||
if _, err = r.ReadAt(ret.Data, offsetAtData); err != nil {
|
||||
return fmt.Errorf("error re-buffering ConstantExpression.Data")
|
||||
}
|
||||
ret.Opcode = opcode
|
||||
return nil
|
||||
}
|
22
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/custom.go
generated
vendored
Normal file
22
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/custom.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// decodeCustomSection deserializes the data **not** associated with the "name" key in SectionIDCustom.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#custom-section%E2%91%A0
|
||||
func decodeCustomSection(r *bytes.Reader, name string, limit uint64) (result *wasm.CustomSection, err error) {
|
||||
buf := make([]byte, limit)
|
||||
_, err = r.Read(buf)
|
||||
|
||||
result = &wasm.CustomSection{
|
||||
Name: name,
|
||||
Data: buf,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
79
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/data.go
generated
vendored
Normal file
79
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/data.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// dataSegmentPrefix represents three types of data segments.
|
||||
//
|
||||
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-section
|
||||
type dataSegmentPrefix = uint32
|
||||
|
||||
const (
|
||||
// dataSegmentPrefixActive is the prefix for the version 1.0 compatible data segment, which is classified as "active" in 2.0.
|
||||
dataSegmentPrefixActive dataSegmentPrefix = 0x0
|
||||
// dataSegmentPrefixPassive prefixes the "passive" data segment as in version 2.0 specification.
|
||||
dataSegmentPrefixPassive dataSegmentPrefix = 0x1
|
||||
// dataSegmentPrefixActiveWithMemoryIndex is the active prefix with memory index encoded which is defined for futur use as of 2.0.
|
||||
dataSegmentPrefixActiveWithMemoryIndex dataSegmentPrefix = 0x2
|
||||
)
|
||||
|
||||
func decodeDataSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.DataSegment) (err error) {
|
||||
dataSegmentPrefx, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read data segment prefix: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
if dataSegmentPrefx != dataSegmentPrefixActive {
|
||||
if err = enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
|
||||
err = fmt.Errorf("non-zero prefix for data segment is invalid as %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch dataSegmentPrefx {
|
||||
case dataSegmentPrefixActive,
|
||||
dataSegmentPrefixActiveWithMemoryIndex:
|
||||
// Active data segment as in
|
||||
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-section
|
||||
if dataSegmentPrefx == 0x2 {
|
||||
d, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read memory index: %v", err)
|
||||
} else if d != 0 {
|
||||
return fmt.Errorf("memory index must be zero but was %d", d)
|
||||
}
|
||||
}
|
||||
|
||||
err = decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpression)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read offset expression: %v", err)
|
||||
}
|
||||
case dataSegmentPrefixPassive:
|
||||
// Passive data segment doesn't need const expr nor memory index encoded.
|
||||
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-section
|
||||
ret.Passive = true
|
||||
default:
|
||||
err = fmt.Errorf("invalid data segment prefix: 0x%x", dataSegmentPrefx)
|
||||
return
|
||||
}
|
||||
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("get the size of vector: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
ret.Init = make([]byte, vs)
|
||||
if _, err = io.ReadFull(r, ret.Init); err != nil {
|
||||
err = fmt.Errorf("read bytes for init: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
193
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/decoder.go
generated
vendored
Normal file
193
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/decoder.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
"github.com/tetratelabs/wazero/internal/wasmdebug"
|
||||
)
|
||||
|
||||
// DecodeModule implements wasm.DecodeModule for the WebAssembly 1.0 (20191205) Binary Format
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-format%E2%91%A0
|
||||
func DecodeModule(
|
||||
binary []byte,
|
||||
enabledFeatures api.CoreFeatures,
|
||||
memoryLimitPages uint32,
|
||||
memoryCapacityFromMax,
|
||||
dwarfEnabled, storeCustomSections bool,
|
||||
) (*wasm.Module, error) {
|
||||
r := bytes.NewReader(binary)
|
||||
|
||||
// Magic number.
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, Magic) {
|
||||
return nil, ErrInvalidMagicNumber
|
||||
}
|
||||
|
||||
// Version.
|
||||
if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, version) {
|
||||
return nil, ErrInvalidVersion
|
||||
}
|
||||
|
||||
memSizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax)
|
||||
|
||||
m := &wasm.Module{}
|
||||
var info, line, str, abbrev, ranges []byte // For DWARF Data.
|
||||
for {
|
||||
// TODO: except custom sections, all others are required to be in order, but we aren't checking yet.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#modules%E2%91%A0%E2%93%AA
|
||||
sectionID, err := r.ReadByte()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("read section id: %w", err)
|
||||
}
|
||||
|
||||
sectionSize, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of section %s: %v", wasm.SectionIDName(sectionID), err)
|
||||
}
|
||||
|
||||
sectionContentStart := r.Len()
|
||||
switch sectionID {
|
||||
case wasm.SectionIDCustom:
|
||||
// First, validate the section and determine if the section for this name has already been set
|
||||
name, nameSize, decodeErr := decodeUTF8(r, "custom section name")
|
||||
if decodeErr != nil {
|
||||
err = decodeErr
|
||||
break
|
||||
} else if sectionSize < nameSize {
|
||||
err = fmt.Errorf("malformed custom section %s", name)
|
||||
break
|
||||
} else if name == "name" && m.NameSection != nil {
|
||||
err = fmt.Errorf("redundant custom section %s", name)
|
||||
break
|
||||
}
|
||||
|
||||
// Now, either decode the NameSection or CustomSection
|
||||
limit := sectionSize - nameSize
|
||||
|
||||
var c *wasm.CustomSection
|
||||
if name != "name" {
|
||||
if storeCustomSections || dwarfEnabled {
|
||||
c, err = decodeCustomSection(r, name, uint64(limit))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read custom section name[%s]: %w", name, err)
|
||||
}
|
||||
m.CustomSections = append(m.CustomSections, c)
|
||||
if dwarfEnabled {
|
||||
switch name {
|
||||
case ".debug_info":
|
||||
info = c.Data
|
||||
case ".debug_line":
|
||||
line = c.Data
|
||||
case ".debug_str":
|
||||
str = c.Data
|
||||
case ".debug_abbrev":
|
||||
abbrev = c.Data
|
||||
case ".debug_ranges":
|
||||
ranges = c.Data
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err = io.CopyN(io.Discard, r, int64(limit)); err != nil {
|
||||
return nil, fmt.Errorf("failed to skip name[%s]: %w", name, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m.NameSection, err = decodeNameSection(r, uint64(limit))
|
||||
}
|
||||
case wasm.SectionIDType:
|
||||
m.TypeSection, err = decodeTypeSection(enabledFeatures, r)
|
||||
case wasm.SectionIDImport:
|
||||
m.ImportSection, m.ImportPerModule, m.ImportFunctionCount, m.ImportGlobalCount, m.ImportMemoryCount, m.ImportTableCount, err = decodeImportSection(r, memSizer, memoryLimitPages, enabledFeatures)
|
||||
if err != nil {
|
||||
return nil, err // avoid re-wrapping the error.
|
||||
}
|
||||
case wasm.SectionIDFunction:
|
||||
m.FunctionSection, err = decodeFunctionSection(r)
|
||||
case wasm.SectionIDTable:
|
||||
m.TableSection, err = decodeTableSection(r, enabledFeatures)
|
||||
case wasm.SectionIDMemory:
|
||||
m.MemorySection, err = decodeMemorySection(r, enabledFeatures, memSizer, memoryLimitPages)
|
||||
case wasm.SectionIDGlobal:
|
||||
if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil {
|
||||
return nil, err // avoid re-wrapping the error.
|
||||
}
|
||||
case wasm.SectionIDExport:
|
||||
m.ExportSection, m.Exports, err = decodeExportSection(r)
|
||||
case wasm.SectionIDStart:
|
||||
if m.StartSection != nil {
|
||||
return nil, errors.New("multiple start sections are invalid")
|
||||
}
|
||||
m.StartSection, err = decodeStartSection(r)
|
||||
case wasm.SectionIDElement:
|
||||
m.ElementSection, err = decodeElementSection(r, enabledFeatures)
|
||||
case wasm.SectionIDCode:
|
||||
m.CodeSection, err = decodeCodeSection(r)
|
||||
case wasm.SectionIDData:
|
||||
m.DataSection, err = decodeDataSection(r, enabledFeatures)
|
||||
case wasm.SectionIDDataCount:
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
|
||||
return nil, fmt.Errorf("data count section not supported as %v", err)
|
||||
}
|
||||
m.DataCountSection, err = decodeDataCountSection(r)
|
||||
default:
|
||||
err = ErrInvalidSectionID
|
||||
}
|
||||
|
||||
readBytes := sectionContentStart - r.Len()
|
||||
if err == nil && int(sectionSize) != readBytes {
|
||||
err = fmt.Errorf("invalid section length: expected to be %d but got %d", sectionSize, readBytes)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("section %s: %v", wasm.SectionIDName(sectionID), err)
|
||||
}
|
||||
}
|
||||
|
||||
if dwarfEnabled {
|
||||
d, _ := dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
|
||||
m.DWARFLines = wasmdebug.NewDWARFLines(d)
|
||||
}
|
||||
|
||||
functionCount, codeCount := m.SectionElementCount(wasm.SectionIDFunction), m.SectionElementCount(wasm.SectionIDCode)
|
||||
if functionCount != codeCount {
|
||||
return nil, fmt.Errorf("function and code section have inconsistent lengths: %d != %d", functionCount, codeCount)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// memorySizer derives min, capacity and max pages from decoded wasm.
|
||||
type memorySizer func(minPages uint32, maxPages *uint32) (min uint32, capacity uint32, max uint32)
|
||||
|
||||
// newMemorySizer sets capacity to minPages unless max is defined and
|
||||
// memoryCapacityFromMax is true.
|
||||
func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memorySizer {
|
||||
return func(minPages uint32, maxPages *uint32) (min, capacity, max uint32) {
|
||||
if maxPages != nil {
|
||||
if memoryCapacityFromMax {
|
||||
return minPages, *maxPages, *maxPages
|
||||
}
|
||||
// This is an invalid value: let it propagate, we will fail later.
|
||||
if *maxPages > wasm.MemoryLimitPages {
|
||||
return minPages, minPages, *maxPages
|
||||
}
|
||||
// This is a valid value, but it goes over the run-time limit: return the limit.
|
||||
if *maxPages > memoryLimitPages {
|
||||
return minPages, minPages, memoryLimitPages
|
||||
}
|
||||
return minPages, minPages, *maxPages
|
||||
}
|
||||
if memoryCapacityFromMax {
|
||||
return minPages, memoryLimitPages, memoryLimitPages
|
||||
}
|
||||
return minPages, minPages, memoryLimitPages
|
||||
}
|
||||
}
|
269
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/element.go
generated
vendored
Normal file
269
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/element.go
generated
vendored
Normal file
@ -0,0 +1,269 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func ensureElementKindFuncRef(r *bytes.Reader) error {
|
||||
elemKind, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read element prefix: %w", err)
|
||||
}
|
||||
if elemKind != 0x0 { // ElemKind is fixed to 0x0 now: https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
|
||||
return fmt.Errorf("element kind must be zero but was 0x%x", elemKind)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeElementInitValueVector(r *bytes.Reader) ([]wasm.Index, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
vec := make([]wasm.Index, vs)
|
||||
for i := range vec {
|
||||
u32, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read function index: %w", err)
|
||||
}
|
||||
|
||||
if u32 >= wasm.MaximumFunctionIndex {
|
||||
return nil, fmt.Errorf("too large function index in Element init: %d", u32)
|
||||
}
|
||||
vec[i] = u32
|
||||
}
|
||||
return vec, nil
|
||||
}
|
||||
|
||||
func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enabledFeatures api.CoreFeatures) ([]wasm.Index, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get the size of constexpr vector: %w", err)
|
||||
}
|
||||
vec := make([]wasm.Index, vs)
|
||||
for i := range vec {
|
||||
var expr wasm.ConstantExpression
|
||||
err := decodeConstantExpression(r, enabledFeatures, &expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch expr.Opcode {
|
||||
case wasm.OpcodeRefFunc:
|
||||
if elemType != wasm.RefTypeFuncref {
|
||||
return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has funcref", wasm.RefTypeName(elemType))
|
||||
}
|
||||
v, _, _ := leb128.LoadUint32(expr.Data)
|
||||
if v >= wasm.MaximumFunctionIndex {
|
||||
return nil, fmt.Errorf("too large function index in Element init: %d", v)
|
||||
}
|
||||
vec[i] = v
|
||||
case wasm.OpcodeRefNull:
|
||||
if elemType != expr.Data[0] {
|
||||
return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has %s",
|
||||
wasm.RefTypeName(elemType), wasm.RefTypeName(expr.Data[0]))
|
||||
}
|
||||
vec[i] = wasm.ElementInitNullReference
|
||||
case wasm.OpcodeGlobalGet:
|
||||
i32, _, _ := leb128.LoadInt32(expr.Data)
|
||||
// Resolving the reference type from globals is done at instantiation phase. See the comment on
|
||||
// wasm.elementInitImportedGlobalReferenceType.
|
||||
vec[i] = wasm.WrapGlobalIndexAsElementInit(wasm.Index(i32))
|
||||
default:
|
||||
return nil, fmt.Errorf("const expr must be either ref.null or ref.func but was %s", wasm.InstructionName(expr.Opcode))
|
||||
}
|
||||
}
|
||||
return vec, nil
|
||||
}
|
||||
|
||||
func decodeElementRefType(r *bytes.Reader) (ret wasm.RefType, err error) {
|
||||
ret, err = r.ReadByte()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read element ref type: %w", err)
|
||||
return
|
||||
}
|
||||
if ret != wasm.RefTypeFuncref && ret != wasm.RefTypeExternref {
|
||||
return 0, errors.New("ref type must be funcref or externref for element as of WebAssembly 2.0")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
// The prefix is explained at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
|
||||
|
||||
// elementSegmentPrefixLegacy is the legacy prefix and is only valid one before CoreFeatureBulkMemoryOperations.
|
||||
elementSegmentPrefixLegacy = iota
|
||||
// elementSegmentPrefixPassiveFuncrefValueVector is the passive element whose indexes are encoded as vec(varint), and reftype is fixed to funcref.
|
||||
elementSegmentPrefixPassiveFuncrefValueVector
|
||||
// elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex is the same as elementSegmentPrefixPassiveFuncrefValueVector but active and table index is encoded.
|
||||
elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex
|
||||
// elementSegmentPrefixDeclarativeFuncrefValueVector is the same as elementSegmentPrefixPassiveFuncrefValueVector but declarative.
|
||||
elementSegmentPrefixDeclarativeFuncrefValueVector
|
||||
// elementSegmentPrefixActiveFuncrefConstExprVector is active whoce reftype is fixed to funcref and indexes are encoded as vec(const_expr).
|
||||
elementSegmentPrefixActiveFuncrefConstExprVector
|
||||
// elementSegmentPrefixPassiveConstExprVector is passive whoce indexes are encoded as vec(const_expr), and reftype is encoded.
|
||||
elementSegmentPrefixPassiveConstExprVector
|
||||
// elementSegmentPrefixPassiveConstExprVector is active whoce indexes are encoded as vec(const_expr), and reftype and table index are encoded.
|
||||
elementSegmentPrefixActiveConstExprVector
|
||||
// elementSegmentPrefixDeclarativeConstExprVector is declarative whoce indexes are encoded as vec(const_expr), and reftype is encoded.
|
||||
elementSegmentPrefixDeclarativeConstExprVector
|
||||
)
|
||||
|
||||
func decodeElementSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.ElementSegment) error {
|
||||
prefix, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read element prefix: %w", err)
|
||||
}
|
||||
|
||||
if prefix != elementSegmentPrefixLegacy {
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
|
||||
return fmt.Errorf("non-zero prefix for element segment is invalid as %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Encoding depends on the prefix and described at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
|
||||
switch prefix {
|
||||
case elementSegmentPrefixLegacy:
|
||||
// Legacy prefix which is WebAssembly 1.0 compatible.
|
||||
err = decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read expr for offset: %w", err)
|
||||
}
|
||||
|
||||
ret.Init, err = decodeElementInitValueVector(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret.Mode = wasm.ElementModeActive
|
||||
ret.Type = wasm.RefTypeFuncref
|
||||
return nil
|
||||
case elementSegmentPrefixPassiveFuncrefValueVector:
|
||||
// Prefix 1 requires funcref.
|
||||
if err = ensureElementKindFuncRef(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret.Init, err = decodeElementInitValueVector(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret.Mode = wasm.ElementModePassive
|
||||
ret.Type = wasm.RefTypeFuncref
|
||||
return nil
|
||||
case elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex:
|
||||
ret.TableIndex, _, err = leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
if ret.TableIndex != 0 {
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
|
||||
return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err)
|
||||
}
|
||||
}
|
||||
|
||||
err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read expr for offset: %w", err)
|
||||
}
|
||||
|
||||
// Prefix 2 requires funcref.
|
||||
if err = ensureElementKindFuncRef(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret.Init, err = decodeElementInitValueVector(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret.Mode = wasm.ElementModeActive
|
||||
ret.Type = wasm.RefTypeFuncref
|
||||
return nil
|
||||
case elementSegmentPrefixDeclarativeFuncrefValueVector:
|
||||
// Prefix 3 requires funcref.
|
||||
if err = ensureElementKindFuncRef(r); err != nil {
|
||||
return err
|
||||
}
|
||||
ret.Init, err = decodeElementInitValueVector(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret.Type = wasm.RefTypeFuncref
|
||||
ret.Mode = wasm.ElementModeDeclarative
|
||||
return nil
|
||||
case elementSegmentPrefixActiveFuncrefConstExprVector:
|
||||
err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read expr for offset: %w", err)
|
||||
}
|
||||
|
||||
ret.Init, err = decodeElementConstExprVector(r, wasm.RefTypeFuncref, enabledFeatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret.Mode = wasm.ElementModeActive
|
||||
ret.Type = wasm.RefTypeFuncref
|
||||
return nil
|
||||
case elementSegmentPrefixPassiveConstExprVector:
|
||||
ret.Type, err = decodeElementRefType(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret.Mode = wasm.ElementModePassive
|
||||
return nil
|
||||
case elementSegmentPrefixActiveConstExprVector:
|
||||
ret.TableIndex, _, err = leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
if ret.TableIndex != 0 {
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
|
||||
return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err)
|
||||
}
|
||||
}
|
||||
err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read expr for offset: %w", err)
|
||||
}
|
||||
|
||||
ret.Type, err = decodeElementRefType(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret.Mode = wasm.ElementModeActive
|
||||
return nil
|
||||
case elementSegmentPrefixDeclarativeConstExprVector:
|
||||
ret.Type, err = decodeElementRefType(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret.Mode = wasm.ElementModeDeclarative
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid element segment prefix: 0x%x", prefix)
|
||||
}
|
||||
}
|
11
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/errors.go
generated
vendored
Normal file
11
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/errors.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package binary
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidByte = errors.New("invalid byte")
|
||||
ErrInvalidMagicNumber = errors.New("invalid magic number")
|
||||
ErrInvalidVersion = errors.New("invalid version header")
|
||||
ErrInvalidSectionID = errors.New("invalid section id")
|
||||
ErrCustomSectionNotFound = errors.New("custom section not found")
|
||||
)
|
32
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/export.go
generated
vendored
Normal file
32
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/export.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func decodeExport(r *bytes.Reader, ret *wasm.Export) (err error) {
|
||||
if ret.Name, _, err = decodeUTF8(r, "export name"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error decoding export kind: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
ret.Type = b
|
||||
switch ret.Type {
|
||||
case wasm.ExternTypeFunc, wasm.ExternTypeTable, wasm.ExternTypeMemory, wasm.ExternTypeGlobal:
|
||||
if ret.Index, _, err = leb128.DecodeUint32(r); err != nil {
|
||||
err = fmt.Errorf("error decoding export index: %w", err)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("%w: invalid byte for exportdesc: %#x", ErrInvalidByte, b)
|
||||
}
|
||||
return
|
||||
}
|
56
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/function.go
generated
vendored
Normal file
56
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/function.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func decodeFunctionType(enabledFeatures api.CoreFeatures, r *bytes.Reader, ret *wasm.FunctionType) (err error) {
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read leading byte: %w", err)
|
||||
}
|
||||
|
||||
if b != 0x60 {
|
||||
return fmt.Errorf("%w: %#x != 0x60", ErrInvalidByte, b)
|
||||
}
|
||||
|
||||
paramCount, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read parameter count: %w", err)
|
||||
}
|
||||
|
||||
paramTypes, err := decodeValueTypes(r, paramCount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read parameter types: %w", err)
|
||||
}
|
||||
|
||||
resultCount, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read result count: %w", err)
|
||||
}
|
||||
|
||||
// Guard >1.0 feature multi-value
|
||||
if resultCount > 1 {
|
||||
if err = enabledFeatures.RequireEnabled(api.CoreFeatureMultiValue); err != nil {
|
||||
return fmt.Errorf("multiple result types invalid as %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
resultTypes, err := decodeValueTypes(r, resultCount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read result types: %w", err)
|
||||
}
|
||||
|
||||
ret.Params = paramTypes
|
||||
ret.Results = resultTypes
|
||||
|
||||
// cache the key for the function type
|
||||
_ = ret.String()
|
||||
|
||||
return nil
|
||||
}
|
50
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/global.go
generated
vendored
Normal file
50
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/global.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// decodeGlobal returns the api.Global decoded with the WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-global
|
||||
func decodeGlobal(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.Global) (err error) {
|
||||
ret.Type, err = decodeGlobalType(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = decodeConstantExpression(r, enabledFeatures, &ret.Init)
|
||||
return
|
||||
}
|
||||
|
||||
// decodeGlobalType returns the wasm.GlobalType decoded with the WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-globaltype
|
||||
func decodeGlobalType(r *bytes.Reader) (wasm.GlobalType, error) {
|
||||
vt, err := decodeValueTypes(r, 1)
|
||||
if err != nil {
|
||||
return wasm.GlobalType{}, fmt.Errorf("read value type: %w", err)
|
||||
}
|
||||
|
||||
ret := wasm.GlobalType{
|
||||
ValType: vt[0],
|
||||
}
|
||||
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return wasm.GlobalType{}, fmt.Errorf("read mutablity: %w", err)
|
||||
}
|
||||
|
||||
switch mut := b; mut {
|
||||
case 0x00: // not mutable
|
||||
case 0x01: // mutable
|
||||
ret.Mutable = true
|
||||
default:
|
||||
return wasm.GlobalType{}, fmt.Errorf("%w for mutability: %#x != 0x00 or 0x01", ErrInvalidByte, mut)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
9
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/header.go
generated
vendored
Normal file
9
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/header.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
package binary
|
||||
|
||||
// Magic is the 4 byte preamble (literally "\0asm") of the binary format
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-magic
|
||||
var Magic = []byte{0x00, 0x61, 0x73, 0x6D}
|
||||
|
||||
// version is format version and doesn't change between known specification versions
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-version
|
||||
var version = []byte{0x01, 0x00, 0x00, 0x00}
|
52
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/import.go
generated
vendored
Normal file
52
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/import.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func decodeImport(
|
||||
r *bytes.Reader,
|
||||
idx uint32,
|
||||
memorySizer memorySizer,
|
||||
memoryLimitPages uint32,
|
||||
enabledFeatures api.CoreFeatures,
|
||||
ret *wasm.Import,
|
||||
) (err error) {
|
||||
if ret.Module, _, err = decodeUTF8(r, "import module"); err != nil {
|
||||
err = fmt.Errorf("import[%d] error decoding module: %w", idx, err)
|
||||
return
|
||||
}
|
||||
|
||||
if ret.Name, _, err = decodeUTF8(r, "import name"); err != nil {
|
||||
err = fmt.Errorf("import[%d] error decoding name: %w", idx, err)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("import[%d] error decoding type: %w", idx, err)
|
||||
return
|
||||
}
|
||||
ret.Type = b
|
||||
switch ret.Type {
|
||||
case wasm.ExternTypeFunc:
|
||||
ret.DescFunc, _, err = leb128.DecodeUint32(r)
|
||||
case wasm.ExternTypeTable:
|
||||
err = decodeTable(r, enabledFeatures, &ret.DescTable)
|
||||
case wasm.ExternTypeMemory:
|
||||
ret.DescMem, err = decodeMemory(r, enabledFeatures, memorySizer, memoryLimitPages)
|
||||
case wasm.ExternTypeGlobal:
|
||||
ret.DescGlobal, err = decodeGlobalType(r)
|
||||
default:
|
||||
err = fmt.Errorf("%w: invalid byte for importdesc: %#x", ErrInvalidByte, b)
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("import[%d] %s[%s.%s]: %w", idx, wasm.ExternTypeName(ret.Type), ret.Module, ret.Name, err)
|
||||
}
|
||||
return
|
||||
}
|
47
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/limits.go
generated
vendored
Normal file
47
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/limits.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
)
|
||||
|
||||
// decodeLimitsType returns the `limitsType` (min, max) decoded with the WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#limits%E2%91%A6
|
||||
//
|
||||
// Extended in threads proposal: https://webassembly.github.io/threads/core/binary/types.html#limits
|
||||
func decodeLimitsType(r *bytes.Reader) (min uint32, max *uint32, shared bool, err error) {
|
||||
var flag byte
|
||||
if flag, err = r.ReadByte(); err != nil {
|
||||
err = fmt.Errorf("read leading byte: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
switch flag {
|
||||
case 0x00, 0x02:
|
||||
min, _, err = leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read min of limit: %v", err)
|
||||
}
|
||||
case 0x01, 0x03:
|
||||
min, _, err = leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read min of limit: %v", err)
|
||||
return
|
||||
}
|
||||
var m uint32
|
||||
if m, _, err = leb128.DecodeUint32(r); err != nil {
|
||||
err = fmt.Errorf("read max of limit: %v", err)
|
||||
} else {
|
||||
max = &m
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("%v for limits: %#x not in (0x00, 0x01, 0x02, 0x03)", ErrInvalidByte, flag)
|
||||
}
|
||||
|
||||
shared = flag == 0x02 || flag == 0x03
|
||||
|
||||
return
|
||||
}
|
42
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/memory.go
generated
vendored
Normal file
42
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/memory.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// decodeMemory returns the api.Memory decoded with the WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-memory
|
||||
func decodeMemory(
|
||||
r *bytes.Reader,
|
||||
enabledFeatures api.CoreFeatures,
|
||||
memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
|
||||
memoryLimitPages uint32,
|
||||
) (*wasm.Memory, error) {
|
||||
min, maxP, shared, err := decodeLimitsType(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if shared {
|
||||
if !enabledFeatures.IsEnabled(experimental.CoreFeaturesThreads) {
|
||||
return nil, fmt.Errorf("shared memory requested but threads feature not enabled")
|
||||
}
|
||||
|
||||
// This restriction may be lifted in the future.
|
||||
// https://webassembly.github.io/threads/core/binary/types.html#memory-types
|
||||
if maxP == nil {
|
||||
return nil, fmt.Errorf("shared memory requires a maximum size to be specified")
|
||||
}
|
||||
}
|
||||
|
||||
min, capacity, max := memorySizer(min, maxP)
|
||||
mem := &wasm.Memory{Min: min, Cap: capacity, Max: max, IsMaxEncoded: maxP != nil, IsShared: shared}
|
||||
|
||||
return mem, mem.Validate(memoryLimitPages)
|
||||
}
|
151
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/names.go
generated
vendored
Normal file
151
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/names.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
const (
|
||||
// subsectionIDModuleName contains only the module name.
|
||||
subsectionIDModuleName = uint8(0)
|
||||
// subsectionIDFunctionNames is a map of indices to function names, in ascending order by function index
|
||||
subsectionIDFunctionNames = uint8(1)
|
||||
// subsectionIDLocalNames contain a map of function indices to a map of local indices to their names, in ascending
|
||||
// order by function and local index
|
||||
subsectionIDLocalNames = uint8(2)
|
||||
)
|
||||
|
||||
// decodeNameSection deserializes the data associated with the "name" key in SectionIDCustom according to the
|
||||
// standard:
|
||||
//
|
||||
// * ModuleName decode from subsection 0
|
||||
// * FunctionNames decode from subsection 1
|
||||
// * LocalNames decode from subsection 2
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namesec
|
||||
func decodeNameSection(r *bytes.Reader, limit uint64) (result *wasm.NameSection, err error) {
|
||||
// TODO: add leb128 functions that work on []byte and offset. While using a reader allows us to reuse reader-based
|
||||
// leb128 functions, it is less efficient, causes untestable code and in some cases more complex vs plain []byte.
|
||||
result = &wasm.NameSection{}
|
||||
|
||||
// subsectionID is decoded if known, and skipped if not
|
||||
var subsectionID uint8
|
||||
// subsectionSize is the length to skip when the subsectionID is unknown
|
||||
var subsectionSize uint32
|
||||
var bytesRead uint64
|
||||
for limit > 0 {
|
||||
if subsectionID, err = r.ReadByte(); err != nil {
|
||||
if err == io.EOF {
|
||||
return result, nil
|
||||
}
|
||||
// TODO: untestable as this can't fail for a reason beside EOF reading a byte from a buffer
|
||||
return nil, fmt.Errorf("failed to read a subsection ID: %w", err)
|
||||
}
|
||||
limit--
|
||||
|
||||
if subsectionSize, bytesRead, err = leb128.DecodeUint32(r); err != nil {
|
||||
return nil, fmt.Errorf("failed to read the size of subsection[%d]: %w", subsectionID, err)
|
||||
}
|
||||
limit -= bytesRead
|
||||
|
||||
switch subsectionID {
|
||||
case subsectionIDModuleName:
|
||||
if result.ModuleName, _, err = decodeUTF8(r, "module name"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case subsectionIDFunctionNames:
|
||||
if result.FunctionNames, err = decodeFunctionNames(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case subsectionIDLocalNames:
|
||||
if result.LocalNames, err = decodeLocalNames(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default: // Skip other subsections.
|
||||
// Note: Not Seek because it doesn't err when given an offset past EOF. Rather, it leads to undefined state.
|
||||
if _, err = io.CopyN(io.Discard, r, int64(subsectionSize)); err != nil {
|
||||
return nil, fmt.Errorf("failed to skip subsection[%d]: %w", subsectionID, err)
|
||||
}
|
||||
}
|
||||
limit -= uint64(subsectionSize)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodeFunctionNames(r *bytes.Reader) (wasm.NameMap, error) {
|
||||
functionCount, err := decodeFunctionCount(r, subsectionIDFunctionNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(wasm.NameMap, functionCount)
|
||||
for i := uint32(0); i < functionCount; i++ {
|
||||
functionIndex, err := decodeFunctionIndex(r, subsectionIDFunctionNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, _, err := decodeUTF8(r, "function[%d] name", functionIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[i] = wasm.NameAssoc{Index: functionIndex, Name: name}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeLocalNames(r *bytes.Reader) (wasm.IndirectNameMap, error) {
|
||||
functionCount, err := decodeFunctionCount(r, subsectionIDLocalNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(wasm.IndirectNameMap, functionCount)
|
||||
for i := uint32(0); i < functionCount; i++ {
|
||||
functionIndex, err := decodeFunctionIndex(r, subsectionIDLocalNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localCount, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the local count for function[%d]: %w", functionIndex, err)
|
||||
}
|
||||
|
||||
locals := make(wasm.NameMap, localCount)
|
||||
for j := uint32(0); j < localCount; j++ {
|
||||
localIndex, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read a local index of function[%d]: %w", functionIndex, err)
|
||||
}
|
||||
|
||||
name, _, err := decodeUTF8(r, "function[%d] local[%d] name", functionIndex, localIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
locals[j] = wasm.NameAssoc{Index: localIndex, Name: name}
|
||||
}
|
||||
result[i] = wasm.NameMapAssoc{Index: functionIndex, NameMap: locals}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeFunctionIndex(r *bytes.Reader, subsectionID uint8) (uint32, error) {
|
||||
functionIndex, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read a function index in subsection[%d]: %w", subsectionID, err)
|
||||
}
|
||||
return functionIndex, nil
|
||||
}
|
||||
|
||||
func decodeFunctionCount(r *bytes.Reader, subsectionID uint8) (uint32, error) {
|
||||
functionCount, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read the function count of subsection[%d]: %w", subsectionID, err)
|
||||
}
|
||||
return functionCount, nil
|
||||
}
|
226
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/section.go
generated
vendored
Normal file
226
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/section.go
generated
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func decodeTypeSection(enabledFeatures api.CoreFeatures, r *bytes.Reader) ([]wasm.FunctionType, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
result := make([]wasm.FunctionType, vs)
|
||||
for i := uint32(0); i < vs; i++ {
|
||||
if err = decodeFunctionType(enabledFeatures, r, &result[i]); err != nil {
|
||||
return nil, fmt.Errorf("read %d-th type: %v", i, err)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// decodeImportSection decodes the decoded import segments plus the count per wasm.ExternType.
|
||||
func decodeImportSection(
|
||||
r *bytes.Reader,
|
||||
memorySizer memorySizer,
|
||||
memoryLimitPages uint32,
|
||||
enabledFeatures api.CoreFeatures,
|
||||
) (result []wasm.Import,
|
||||
perModule map[string][]*wasm.Import,
|
||||
funcCount, globalCount, memoryCount, tableCount wasm.Index, err error,
|
||||
) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("get size of vector: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
perModule = make(map[string][]*wasm.Import)
|
||||
result = make([]wasm.Import, vs)
|
||||
for i := uint32(0); i < vs; i++ {
|
||||
imp := &result[i]
|
||||
if err = decodeImport(r, i, memorySizer, memoryLimitPages, enabledFeatures, imp); err != nil {
|
||||
return
|
||||
}
|
||||
switch imp.Type {
|
||||
case wasm.ExternTypeFunc:
|
||||
imp.IndexPerType = funcCount
|
||||
funcCount++
|
||||
case wasm.ExternTypeGlobal:
|
||||
imp.IndexPerType = globalCount
|
||||
globalCount++
|
||||
case wasm.ExternTypeMemory:
|
||||
imp.IndexPerType = memoryCount
|
||||
memoryCount++
|
||||
case wasm.ExternTypeTable:
|
||||
imp.IndexPerType = tableCount
|
||||
tableCount++
|
||||
}
|
||||
perModule[imp.Module] = append(perModule[imp.Module], imp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodeFunctionSection(r *bytes.Reader) ([]uint32, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
result := make([]uint32, vs)
|
||||
for i := uint32(0); i < vs; i++ {
|
||||
if result[i], _, err = leb128.DecodeUint32(r); err != nil {
|
||||
return nil, fmt.Errorf("get type index: %w", err)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func decodeTableSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]wasm.Table, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading size")
|
||||
}
|
||||
if vs > 1 {
|
||||
if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
|
||||
return nil, fmt.Errorf("at most one table allowed in module as %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
ret := make([]wasm.Table, vs)
|
||||
for i := range ret {
|
||||
err = decodeTable(r, enabledFeatures, &ret[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func decodeMemorySection(
|
||||
r *bytes.Reader,
|
||||
enabledFeatures api.CoreFeatures,
|
||||
memorySizer memorySizer,
|
||||
memoryLimitPages uint32,
|
||||
) (*wasm.Memory, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading size")
|
||||
}
|
||||
if vs > 1 {
|
||||
return nil, fmt.Errorf("at most one memory allowed in module, but read %d", vs)
|
||||
} else if vs == 0 {
|
||||
// memory count can be zero.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return decodeMemory(r, enabledFeatures, memorySizer, memoryLimitPages)
|
||||
}
|
||||
|
||||
func decodeGlobalSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]wasm.Global, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
result := make([]wasm.Global, vs)
|
||||
for i := uint32(0); i < vs; i++ {
|
||||
if err = decodeGlobal(r, enabledFeatures, &result[i]); err != nil {
|
||||
return nil, fmt.Errorf("global[%d]: %w", i, err)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeExportSection(r *bytes.Reader) ([]wasm.Export, map[string]*wasm.Export, error) {
|
||||
vs, _, sizeErr := leb128.DecodeUint32(r)
|
||||
if sizeErr != nil {
|
||||
return nil, nil, fmt.Errorf("get size of vector: %v", sizeErr)
|
||||
}
|
||||
|
||||
exportMap := make(map[string]*wasm.Export, vs)
|
||||
exportSection := make([]wasm.Export, vs)
|
||||
for i := wasm.Index(0); i < vs; i++ {
|
||||
export := &exportSection[i]
|
||||
err := decodeExport(r, export)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("read export: %w", err)
|
||||
}
|
||||
if _, ok := exportMap[export.Name]; ok {
|
||||
return nil, nil, fmt.Errorf("export[%d] duplicates name %q", i, export.Name)
|
||||
} else {
|
||||
exportMap[export.Name] = export
|
||||
}
|
||||
}
|
||||
return exportSection, exportMap, nil
|
||||
}
|
||||
|
||||
func decodeStartSection(r *bytes.Reader) (*wasm.Index, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get function index: %w", err)
|
||||
}
|
||||
return &vs, nil
|
||||
}
|
||||
|
||||
func decodeElementSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]wasm.ElementSegment, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
result := make([]wasm.ElementSegment, vs)
|
||||
for i := uint32(0); i < vs; i++ {
|
||||
if err = decodeElementSegment(r, enabledFeatures, &result[i]); err != nil {
|
||||
return nil, fmt.Errorf("read element: %w", err)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeCodeSection(r *bytes.Reader) ([]wasm.Code, error) {
|
||||
codeSectionStart := uint64(r.Len())
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
result := make([]wasm.Code, vs)
|
||||
for i := uint32(0); i < vs; i++ {
|
||||
err = decodeCode(r, codeSectionStart, &result[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %d-th code segment: %v", i, err)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeDataSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]wasm.DataSegment, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %w", err)
|
||||
}
|
||||
|
||||
result := make([]wasm.DataSegment, vs)
|
||||
for i := uint32(0); i < vs; i++ {
|
||||
if err = decodeDataSegment(r, enabledFeatures, &result[i]); err != nil {
|
||||
return nil, fmt.Errorf("read data segment: %w", err)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeDataCountSection(r *bytes.Reader) (count *uint32, err error) {
|
||||
v, _, err := leb128.DecodeUint32(r)
|
||||
if err != nil && err != io.EOF {
|
||||
// data count is optional, so EOF is fine.
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
}
|
43
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/table.go
generated
vendored
Normal file
43
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/table.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// decodeTable returns the wasm.Table decoded with the WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-table
|
||||
func decodeTable(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.Table) (err error) {
|
||||
ret.Type, err = r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read leading byte: %v", err)
|
||||
}
|
||||
|
||||
if ret.Type != wasm.RefTypeFuncref {
|
||||
if err = enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
|
||||
return fmt.Errorf("table type funcref is invalid: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var shared bool
|
||||
ret.Min, ret.Max, shared, err = decodeLimitsType(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read limits: %v", err)
|
||||
}
|
||||
if ret.Min > wasm.MaximumFunctionIndex {
|
||||
return fmt.Errorf("table min must be at most %d", wasm.MaximumFunctionIndex)
|
||||
}
|
||||
if ret.Max != nil {
|
||||
if *ret.Max < ret.Min {
|
||||
return fmt.Errorf("table size minimum must not be greater than maximum")
|
||||
}
|
||||
}
|
||||
if shared {
|
||||
return fmt.Errorf("tables cannot be marked as shared")
|
||||
}
|
||||
return
|
||||
}
|
60
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/value.go
generated
vendored
Normal file
60
vendor/github.com/tetratelabs/wazero/internal/wasm/binary/value.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func decodeValueTypes(r *bytes.Reader, num uint32) ([]wasm.ValueType, error) {
|
||||
if num == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ret := make([]wasm.ValueType, num)
|
||||
_, err := io.ReadFull(r, ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range ret {
|
||||
switch v {
|
||||
case wasm.ValueTypeI32, wasm.ValueTypeF32, wasm.ValueTypeI64, wasm.ValueTypeF64,
|
||||
wasm.ValueTypeExternref, wasm.ValueTypeFuncref, wasm.ValueTypeV128:
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid value type: %d", v)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// decodeUTF8 decodes a size prefixed string from the reader, returning it and the count of bytes read.
|
||||
// contextFormat and contextArgs apply an error format when present
|
||||
func decodeUTF8(r *bytes.Reader, contextFormat string, contextArgs ...interface{}) (string, uint32, error) {
|
||||
size, sizeOfSize, err := leb128.DecodeUint32(r)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("failed to read %s size: %w", fmt.Sprintf(contextFormat, contextArgs...), err)
|
||||
}
|
||||
|
||||
if size == 0 {
|
||||
return "", uint32(sizeOfSize), nil
|
||||
}
|
||||
|
||||
buf := make([]byte, size)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return "", 0, fmt.Errorf("failed to read %s: %w", fmt.Sprintf(contextFormat, contextArgs...), err)
|
||||
}
|
||||
|
||||
if !utf8.Valid(buf) {
|
||||
return "", 0, fmt.Errorf("%s is not valid UTF-8", fmt.Sprintf(contextFormat, contextArgs...))
|
||||
}
|
||||
|
||||
// TODO: use unsafe.String after flooring Go 1.20.
|
||||
ret := *(*string)(unsafe.Pointer(&buf))
|
||||
return ret, size + uint32(sizeOfSize), nil
|
||||
}
|
Reference in New Issue
Block a user