Skip to content

Commit bd70b0b

Browse files
author
dave sinclair
committed
Added variable length Buffer.read* methods
- added `readIntBE`, `readIntLE`, `readUIntBE`, and `readUIntLE`. - also refactored out some common methods to create errors
1 parent 2ae4cd2 commit bd70b0b

File tree

2 files changed

+356
-2
lines changed

2 files changed

+356
-2
lines changed

buffer/buffer.go

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,36 @@ func (b *Buffer) readInt32LE(call goja.FunctionCall) goja.Value {
524524
return b.r.ToValue(value)
525525
}
526526

527+
// readIntBE reads a big-endian signed integer of variable byte length
528+
func (b *Buffer) readIntBE(call goja.FunctionCall) goja.Value {
529+
bb := Bytes(b.r, call.This)
530+
offset, byteLength := b.getVariableLengthReadArguments(call, bb)
531+
532+
var value int64
533+
for i := int64(0); i < byteLength; i++ {
534+
value = (value << 8) | int64(bb[offset+i])
535+
}
536+
537+
value = signExtend(value, byteLength)
538+
539+
return b.r.ToValue(value)
540+
}
541+
542+
// readIntLE reads a little-endian signed integer of variable byte length
543+
func (b *Buffer) readIntLE(call goja.FunctionCall) goja.Value {
544+
bb := Bytes(b.r, call.This)
545+
offset, byteLength := b.getVariableLengthReadArguments(call, bb)
546+
547+
var value int64
548+
for i := byteLength - 1; i >= 0; i-- {
549+
value = (value << 8) | int64(bb[offset+i])
550+
}
551+
552+
value = signExtend(value, byteLength)
553+
554+
return b.r.ToValue(value)
555+
}
556+
527557
// readUInt8 reads an 8-bit unsigned integer from the buffer
528558
func (b *Buffer) readUInt8(call goja.FunctionCall) goja.Value {
529559
bb := Bytes(b.r, call.This)
@@ -569,6 +599,32 @@ func (b *Buffer) readUInt32LE(call goja.FunctionCall) goja.Value {
569599
return b.r.ToValue(value)
570600
}
571601

602+
// readUIntBE reads a big-endian unsigned integer of variable byte length
603+
func (b *Buffer) readUIntBE(call goja.FunctionCall) goja.Value {
604+
bb := Bytes(b.r, call.This)
605+
offset, byteLength := b.getVariableLengthReadArguments(call, bb)
606+
607+
var value uint64
608+
for i := int64(0); i < byteLength; i++ {
609+
value = (value << 8) | uint64(bb[offset+i])
610+
}
611+
612+
return b.r.ToValue(value)
613+
}
614+
615+
// readUIntLE reads a little-endian unsigned integer of variable byte length
616+
func (b *Buffer) readUIntLE(call goja.FunctionCall) goja.Value {
617+
bb := Bytes(b.r, call.This)
618+
offset, byteLength := b.getVariableLengthReadArguments(call, bb)
619+
620+
var value uint64
621+
for i := byteLength - 1; i >= 0; i-- {
622+
value = (value << 8) | uint64(bb[offset+i])
623+
}
624+
625+
return b.r.ToValue(value)
626+
}
627+
572628
func (b *Buffer) getOffsetArgument(call goja.FunctionCall, argIndex int, bb []byte, numBytes int64) int64 {
573629
arg := call.Argument(argIndex)
574630
var offset int64
@@ -578,16 +634,56 @@ func (b *Buffer) getOffsetArgument(call goja.FunctionCall, argIndex int, bb []by
578634
// optional arg that defaults to zero
579635
offset = 0
580636
} else {
581-
panic(errors.NewTypeError(b.r, errors.ErrCodeInvalidArgType, "The \"offset\" argument must be of type number."))
637+
panic(b.newArgumentNotNumberTypeError("offset"))
582638
}
583639

584640
if offset < 0 || offset+numBytes > int64(len(bb)) {
585-
panic(errors.NewError(b.r, nil, errors.ErrCodedOutOfRange, "The value of \"offset\" %d is out of range", offset))
641+
panic(b.newArgumentOutOfRangeError("offset", offset))
586642
}
587643

588644
return offset
589645
}
590646

647+
func (b *Buffer) getVariableLengthReadArguments(call goja.FunctionCall, bb []byte) (int64, int64) {
648+
offset := b.getRequiredIntegerArgument(call, "offset", 0)
649+
byteLength := b.getRequiredIntegerArgument(call, "byteLength", 1)
650+
651+
if byteLength < 1 || byteLength > 6 {
652+
panic(b.newArgumentOutOfRangeError("byteLength", byteLength))
653+
}
654+
if offset < 0 || offset+byteLength > int64(len(bb)) {
655+
panic(b.newArgumentOutOfRangeError("offset", offset))
656+
}
657+
658+
return offset, byteLength
659+
}
660+
661+
func signExtend(value int64, numBytes int64) int64 {
662+
// we don't have to turn this to a uint64 first because numBytes < 8 so
663+
// the sign bit will never pushed out of the int64 range
664+
return (value << (64 - 8*numBytes)) >> (64 - 8*numBytes)
665+
}
666+
667+
func (b *Buffer) getRequiredIntegerArgument(call goja.FunctionCall, name string, argIndex int) int64 {
668+
arg := call.Argument(argIndex)
669+
if isNumber(arg) {
670+
return arg.ToInteger()
671+
}
672+
if goja.IsUndefined(arg) {
673+
panic(errors.NewTypeError(b.r, errors.ErrCodeInvalidArgType, "The \"%s\" argument is required.", name))
674+
}
675+
676+
panic(b.newArgumentNotNumberTypeError(name))
677+
}
678+
679+
func (b *Buffer) newArgumentNotNumberTypeError(name string) *goja.Object {
680+
return errors.NewTypeError(b.r, errors.ErrCodeInvalidArgType, "The \"%s\" argument must be of type number.", name)
681+
}
682+
683+
func (b *Buffer) newArgumentOutOfRangeError(name string, v int64) *goja.Object {
684+
return errors.NewError(b.r, nil, errors.ErrCodedOutOfRange, "The value of \"%s\" %d is out of range", name, v)
685+
}
686+
591687
func Require(runtime *goja.Runtime, module *goja.Object) {
592688
b := &Buffer{r: runtime}
593689
uint8Array := runtime.Get("Uint8Array")
@@ -622,11 +718,15 @@ func Require(runtime *goja.Runtime, module *goja.Object) {
622718
proto.Set("readInt16LE", b.readInt16LE)
623719
proto.Set("readInt32BE", b.readInt32BE)
624720
proto.Set("readInt32LE", b.readInt32LE)
721+
proto.Set("readIntBE", b.readIntBE)
722+
proto.Set("readIntLE", b.readIntLE)
625723
proto.Set("readUInt8", b.readUInt8)
626724
proto.Set("readUInt16BE", b.readUInt16BE)
627725
proto.Set("readUInt16LE", b.readUInt16LE)
628726
proto.Set("readUInt32BE", b.readUInt32BE)
629727
proto.Set("readUInt32LE", b.readUInt32LE)
728+
proto.Set("readUIntBE", b.readUIntBE)
729+
proto.Set("readUIntLE", b.readUIntLE)
630730

631731
ctor.Set("prototype", proto)
632732
ctor.Set("poolSize", 8192)

0 commit comments

Comments
 (0)