blob: 462ed77766f423d0ac41bd651f57874109bc876e [file] [log] [blame]
// Copyright 2014 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 seq
import (
"bytes"
"fmt"
"runtime"
"unsafe"
)
// Buffer is a set of arguments or return values from a function call
// across the language boundary. Encoding is machine-dependent.
type Buffer struct {
Data []byte
Offset int // position of next read/write from Data
}
func (b *Buffer) String() string {
// Debugging.
var buf bytes.Buffer
fmt.Fprintf(&buf, "seq{Off=%d, Len=%d Data=", b.Offset, len(b.Data))
const hextable = "0123456789abcdef"
for i, v := range b.Data {
if i > 0 {
buf.WriteByte(':')
}
buf.WriteByte(hextable[v>>4])
buf.WriteByte(hextable[v&0x0f])
}
buf.WriteByte('}')
return buf.String()
}
func (b *Buffer) panic(need int) {
panic(fmt.Sprintf("need %d bytes: %s", need, b))
}
func (b *Buffer) grow(need int) {
size := len(b.Data)
if size == 0 {
size = 2
}
for size < need {
size *= 2
}
data := make([]byte, size+len(b.Data))
copy(data, b.Data[:b.Offset])
b.Data = data
}
// align returns the aligned offset.
func align(offset, alignment int) int {
pad := offset % alignment
if pad > 0 {
pad = alignment - pad
}
return pad + offset
}
func (b *Buffer) ReadInt32() int32 {
offset := align(b.Offset, 4)
if len(b.Data)-offset < 4 {
b.panic(4)
}
v := *(*int32)(unsafe.Pointer(&b.Data[offset]))
b.Offset = offset + 4
return v
}
func (b *Buffer) ReadInt64() int64 {
offset := align(b.Offset, 8)
if len(b.Data)-offset < 8 {
b.panic(8)
}
v := *(*int64)(unsafe.Pointer(&b.Data[offset]))
b.Offset = offset + 8
return v
}
// TODO(hyangah): int8, int16?
func (b *Buffer) ReadInt() int {
return int(b.ReadInt64())
}
func (b *Buffer) ReadFloat32() float32 {
offset := align(b.Offset, 4)
if len(b.Data)-offset < 4 {
b.panic(4)
}
v := *(*float32)(unsafe.Pointer(&b.Data[offset]))
b.Offset = offset + 4
return v
}
func (b *Buffer) ReadFloat64() float64 {
offset := align(b.Offset, 8)
if len(b.Data)-offset < 8 {
b.panic(8)
}
v := *(*float64)(unsafe.Pointer(&b.Data[offset]))
b.Offset = offset + 8
return v
}
func (b *Buffer) ReadByteArray() []byte {
sz := b.ReadInt64()
if sz == 0 {
return nil
}
ptr := b.ReadInt64()
org := (*[1 << 30]byte)(unsafe.Pointer(uintptr(ptr)))[:sz]
// Make a copy managed by Go, so the returned byte array can be
// used safely in Go.
slice := make([]byte, sz)
copy(slice, org)
return slice
}
func (b *Buffer) ReadRef() *Ref {
ref := &Ref{b.ReadInt32()}
if ref.Num > 0 {
// This is a foreign object reference.
// Track its lifetime with a finalizer.
runtime.SetFinalizer(ref, FinalizeRef)
}
return ref
}
func (b *Buffer) WriteInt32(v int32) {
offset := align(b.Offset, 4)
if len(b.Data)-offset < 4 {
b.grow(offset + 4 - len(b.Data))
}
*(*int32)(unsafe.Pointer(&b.Data[offset])) = v
b.Offset = offset + 4
}
func (b *Buffer) WriteInt64(v int64) {
offset := align(b.Offset, 8)
if len(b.Data)-offset < 8 {
b.grow(offset + 8 - len(b.Data))
}
*(*int64)(unsafe.Pointer(&b.Data[offset])) = v
b.Offset = offset + 8
}
func (b *Buffer) WriteInt(v int) {
b.WriteInt64(int64(v))
}
func (b *Buffer) WriteFloat32(v float32) {
offset := align(b.Offset, 4)
if len(b.Data)-offset < 4 {
b.grow(offset + 4 - len(b.Data))
}
*(*float32)(unsafe.Pointer(&b.Data[offset])) = v
b.Offset = offset + 4
}
func (b *Buffer) WriteFloat64(v float64) {
offset := align(b.Offset, 8)
if len(b.Data)-offset < 8 {
b.grow(offset + 8 - len(b.Data))
}
*(*float64)(unsafe.Pointer(&b.Data[offset])) = v
b.Offset = offset + 8
}
func (b *Buffer) WriteByteArray(byt []byte) {
sz := len(byt)
if sz == 0 {
b.WriteInt64(int64(sz))
return
}
ptr := uintptr(unsafe.Pointer(&byt[0]))
b.WriteInt64(int64(sz))
b.WriteInt64(int64(ptr))
return
}
func (b *Buffer) WriteGoRef(obj interface{}) {
refs.Lock()
num := refs.refs[obj]
if num == 0 {
num = refs.next
refs.next--
if refs.next > 0 {
panic("refs.next underflow")
}
refs.refs[obj] = num
refs.objs[num] = obj
}
refs.Unlock()
b.WriteInt32(int32(num))
}
/* TODO: Will we need it?
func (b *Buffer) WriteRef(ref *Ref) {
b.WriteInt32(ref.Num)
}
*/