Index: third_party/mojo/src/mojo/public/go/bindings/encoder.go |
diff --git a/third_party/mojo/src/mojo/public/go/bindings/encoder.go b/third_party/mojo/src/mojo/public/go/bindings/encoder.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..210d9b01a0ed5a39d74065ad39e1e4f46709cf05 |
--- /dev/null |
+++ b/third_party/mojo/src/mojo/public/go/bindings/encoder.go |
@@ -0,0 +1,319 @@ |
+// Copyright 2015 The Chromium 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 bindings |
+ |
+import ( |
+ "encoding/binary" |
+ "fmt" |
+ "math" |
+ |
+ "mojo/public/go/system" |
+) |
+ |
+// encodingState has information required to encode/decode a one-level value. |
+type encodingState struct { |
+ // Index of the first unprocessed byte. |
+ offset int |
+ |
+ // Index of the first unprocessed bit of buffer[offset] byte. |
+ bitOffset uint32 |
+ |
+ // Index of the first byte after the claimed buffer block for the current |
+ // one-level value. |
+ limit int |
+ |
+ // Element size in bits of the current one-level array, 0 for other types. |
+ elementBitSize uint32 |
+ |
+ // Number of elements declared in the data header for the current one-level |
+ // value. |
+ elements uint32 |
+ |
+ // Number of elements already encoded/decoded of the current one-level |
+ // value. |
+ elementsProcessed uint32 |
+} |
+ |
+func (s *encodingState) skipBits(count uint32) { |
+ s.bitOffset += count |
+ s.offset += int(s.bitOffset >> 3) // equal to s.bitOffset / 8 |
+ s.bitOffset &= 7 // equal to s.bitOffset % 8 |
+} |
+ |
+func (s *encodingState) skipBytes(count int) { |
+ s.bitOffset = 0 |
+ s.offset += count |
+} |
+ |
+// Encoder is a helper to encode mojo complex elements into mojo archive format. |
+type Encoder struct { |
+ // Buffer containing encoded data. |
+ buf []byte |
+ |
+ // Index of the first unclaimed byte in buf. |
+ end int |
+ |
+ // Array containing encoded handles. |
+ handles []system.UntypedHandle |
+ |
+ // A stack of encoder states matching current one-level value stack |
+ // of the encoding data structure. |
+ stateStack []encodingState |
+} |
+ |
+func align(size, alignment int) int { |
+ return ((size - 1) | (alignment - 1)) + 1 |
+} |
+ |
+// bytesForBits returns minimum number of bytes required to store provided |
+// number of bits. |
+func bytesForBits(bits uint64) int { |
+ return int((bits + 7) / 8) |
+} |
+ |
+func ensureElementBitSizeAndCapacity(state *encodingState, bitSize uint32) error { |
+ if state == nil { |
+ return fmt.Errorf("empty state stack") |
+ } |
+ if state.elementBitSize > 0 && state.elementBitSize != bitSize { |
+ return fmt.Errorf("unexpected element bit size: expected %d, but got %d", state.elementBitSize, bitSize) |
+ } |
+ if state.elementsProcessed >= state.elements { |
+ return fmt.Errorf("can't process more than elements defined in header(%d)", state.elements) |
+ } |
+ byteSize := bytesForBits(uint64(state.bitOffset + bitSize)) |
+ if align(state.offset+byteSize, byteSize) > state.limit { |
+ return fmt.Errorf("buffer size limit exceeded") |
+ } |
+ return nil |
+} |
+ |
+// claimData claims a block of |size| bytes for a one-level value, resizing |
+// buffer if needed. |
+func (e *Encoder) claimData(size int) { |
+ e.end += size |
+ if e.end < len(e.buf) { |
+ return |
+ } |
+ newLen := e.end |
+ if l := 2 * len(e.buf); newLen < l { |
+ newLen = l |
+ } |
+ tmp := make([]byte, newLen) |
+ copy(tmp, e.buf) |
+ e.buf = tmp |
+} |
+ |
+func (e *Encoder) popState() { |
+ if len(e.stateStack) != 0 { |
+ e.stateStack = e.stateStack[:len(e.stateStack)-1] |
+ } |
+} |
+ |
+func (e *Encoder) pushState(header DataHeader, elementBitSize uint32) { |
+ oldEnd := e.end |
+ e.claimData(align(int(header.Size), defaultAlignment)) |
+ e.stateStack = append(e.stateStack, encodingState{ |
+ offset: oldEnd, |
+ limit: e.end, |
+ elementBitSize: elementBitSize, |
+ elements: header.Elements, |
+ }) |
+ e.writeDataHeader(header) |
+} |
+ |
+// state returns encoder state of the top-level value. |
+func (e *Encoder) state() *encodingState { |
+ if len(e.stateStack) == 0 { |
+ return nil |
+ } |
+ return &e.stateStack[len(e.stateStack)-1] |
+} |
+ |
+// NewEncoder returns a new instance of encoder. |
+func NewEncoder() *Encoder { |
+ return &Encoder{} |
+} |
+ |
+// StartArray starts encoding an array and writes its data header. |
+// Note: it doesn't write a pointer to the encoded array. |
+// Call |Finish()| after writing all array elements. |
+func (e *Encoder) StartArray(length, elementBitSize uint32) { |
+ dataSize := dataHeaderSize + bytesForBits(uint64(length)*uint64(elementBitSize)) |
+ header := DataHeader{uint32(dataSize), length} |
+ e.pushState(header, elementBitSize) |
+} |
+ |
+// StartMap starts encoding a map and writes its data header. |
+// Note: it doesn't write a pointer to the encoded map. |
+// Call |Finish()| after writing keys array and values array. |
+func (e *Encoder) StartMap() { |
+ e.pushState(mapHeader, pointerBitSize) |
+} |
+ |
+// StartStruct starts encoding a struct and writes its data header. |
+// Note: it doesn't write a pointer to the encoded struct. |
+// Call |Finish()| after writing all fields. |
+func (e *Encoder) StartStruct(size, numFields uint32) { |
+ dataSize := dataHeaderSize + int(size) |
+ header := DataHeader{uint32(dataSize), numFields} |
+ e.pushState(header, 0) |
+} |
+ |
+func (e *Encoder) writeDataHeader(header DataHeader) { |
+ binary.LittleEndian.PutUint32(e.buf[e.state().offset:], header.Size) |
+ binary.LittleEndian.PutUint32(e.buf[e.state().offset+4:], header.Elements) |
+ e.state().offset += 8 |
+} |
+ |
+// Finish indicates the encoder that you have finished writing elements of |
+// a one-level value. |
+func (e *Encoder) Finish() error { |
+ if e.state() == nil { |
+ return fmt.Errorf("state stack is empty") |
+ } |
+ if e.state().elementsProcessed != e.state().elements { |
+ return fmt.Errorf("unexpected number of elements written: defined in header %d, but written %d", e.state().elements, e.state().elementsProcessed) |
+ } |
+ e.popState() |
+ return nil |
+} |
+ |
+// Data returns an encoded message with attached handles. |
+// Call this method after finishing encoding of a value. |
+func (e *Encoder) Data() ([]byte, []system.UntypedHandle, error) { |
+ if len(e.stateStack) != 0 { |
+ return nil, nil, fmt.Errorf("can't return data when encoder has non-empty state stack") |
+ } |
+ return e.buf[:e.end], e.handles, nil |
+} |
+ |
+// WriteBool writes a bool value. |
+func (e *Encoder) WriteBool(value bool) error { |
+ if err := ensureElementBitSizeAndCapacity(e.state(), 1); err != nil { |
+ return err |
+ } |
+ if value { |
+ e.buf[e.state().offset] |= 1 << e.state().bitOffset |
+ } |
+ e.state().skipBits(1) |
+ e.state().elementsProcessed++ |
+ return nil |
+} |
+ |
+// WriteBool writes an int8 value. |
+func (e *Encoder) WriteInt8(value int8) error { |
+ return e.WriteUint8(uint8(value)) |
+} |
+ |
+// WriteUint8 writes an uint8 value. |
+func (e *Encoder) WriteUint8(value uint8) error { |
+ if err := ensureElementBitSizeAndCapacity(e.state(), 8); err != nil { |
+ return err |
+ } |
+ e.buf[e.state().offset] = value |
+ e.state().skipBytes(1) |
+ e.state().elementsProcessed++ |
+ return nil |
+} |
+ |
+// WriteInt16 writes an int16 value. |
+func (e *Encoder) WriteInt16(value int16) error { |
+ return e.WriteUint16(uint16(value)) |
+} |
+ |
+// WriteUint16 writes an uint16 value. |
+func (e *Encoder) WriteUint16(value uint16) error { |
+ if err := ensureElementBitSizeAndCapacity(e.state(), 16); err != nil { |
+ return err |
+ } |
+ e.state().offset = align(e.state().offset, 2) |
+ binary.LittleEndian.PutUint16(e.buf[e.state().offset:], value) |
+ e.state().skipBytes(2) |
+ e.state().elementsProcessed++ |
+ return nil |
+} |
+ |
+// WriteInt32 writes an int32 value. |
+func (e *Encoder) WriteInt32(value int32) error { |
+ return e.WriteUint32(uint32(value)) |
+} |
+ |
+// WriteUint32 writes an uint32 value. |
+func (e *Encoder) WriteUint32(value uint32) error { |
+ if err := ensureElementBitSizeAndCapacity(e.state(), 32); err != nil { |
+ return err |
+ } |
+ e.state().offset = align(e.state().offset, 4) |
+ binary.LittleEndian.PutUint32(e.buf[e.state().offset:], value) |
+ e.state().skipBytes(4) |
+ e.state().elementsProcessed++ |
+ return nil |
+} |
+ |
+// WriteInt64 writes an int64 value. |
+func (e *Encoder) WriteInt64(value int64) error { |
+ return e.WriteUint64(uint64(value)) |
+} |
+ |
+// WriteUint64 writes an uint64 value. |
+func (e *Encoder) WriteUint64(value uint64) error { |
+ if err := ensureElementBitSizeAndCapacity(e.state(), 64); err != nil { |
+ return err |
+ } |
+ e.state().offset = align(e.state().offset, 8) |
+ binary.LittleEndian.PutUint64(e.buf[e.state().offset:], value) |
+ e.state().skipBytes(8) |
+ e.state().elementsProcessed++ |
+ return nil |
+} |
+ |
+// WriteFloat32 writes a float32 value. |
+func (e *Encoder) WriteFloat32(value float32) error { |
+ return e.WriteUint32(math.Float32bits(value)) |
+} |
+ |
+// WriteFloat64 writes a float64 value. |
+func (e *Encoder) WriteFloat64(value float64) error { |
+ return e.WriteUint64(math.Float64bits(value)) |
+} |
+ |
+// WriteNullPointer writes a null pointer. |
+func (e *Encoder) WriteNullPointer() error { |
+ return e.WriteUint64(0) |
+} |
+ |
+// WriteString writes a string value. It doesn't write a pointer to the encoded |
+// string. |
+func (e *Encoder) WriteString(value string) error { |
+ bytes := []byte(value) |
+ e.StartArray(uint32(len(bytes)), 8) |
+ for _, b := range bytes { |
+ if err := e.WriteUint8(b); err != nil { |
+ return err |
+ } |
+ } |
+ return e.Finish() |
+} |
+ |
+// WritePointer writes a pointer to first unclaimed byte index. |
+func (e *Encoder) WritePointer() error { |
+ return e.WriteUint64(uint64(e.end - e.state().offset)) |
+} |
+ |
+// WriteInvalidHandle an invalid handle. |
+func (e *Encoder) WriteInvalidHandle() error { |
+ return e.WriteInt32(-1) |
+} |
+ |
+// WriteHandle writes a handle and invalidates the passed handle object. |
+func (e *Encoder) WriteHandle(handle system.Handle) error { |
+ if !handle.IsValid() { |
+ return fmt.Errorf("can't write an invalid handle") |
+ } |
+ UntypedHandle := handle.ToUntypedHandle() |
+ e.handles = append(e.handles, UntypedHandle) |
+ return e.WriteUint32(uint32(len(e.handles) - 1)) |
+} |