Files

211 lines
5.3 KiB
Go

// Package nvda2cthulhu accepts the local WebSocket protocol used by NVDA
// compatibility bridges and routes it to Cthulhu's speech and braille queues.
package nvda2cthulhu
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
)
type messagePackBinary string
func decodeMessagePack(reader *bytes.Reader) (any, error) {
return decodeMessagePackDepth(reader, 0)
}
func decodeMessagePackDepth(reader *bytes.Reader, depth int) (any, error) {
if depth > 64 {
return nil, errors.New("MessagePack nesting exceeds limit")
}
code, err := reader.ReadByte()
if err != nil {
return nil, err
}
switch {
case code <= 0x7f:
return int64(code), nil
case code >= 0xe0:
return int64(int8(code)), nil
case code >= 0x80 && code <= 0x8f:
return decodeMessagePackMap(reader, int(code&0x0f), depth)
case code >= 0x90 && code <= 0x9f:
return decodeMessagePackArray(reader, int(code&0x0f), depth)
case code >= 0xa0 && code <= 0xbf:
return decodeMessagePackString(reader, int(code&0x1f))
case code == 0xc0:
return nil, nil
case code == 0xc2:
return false, nil
case code == 0xc3:
return true, nil
case code == 0xc4 || code == 0xc5 || code == 0xc6:
length, err := decodeLength(reader, code, 0xc4)
if err != nil {
return nil, err
}
value := make([]byte, length)
if _, err := io.ReadFull(reader, value); err != nil {
return nil, err
}
return messagePackBinary(value), nil
case code >= 0xca && code <= 0xcb:
return decodeMessagePackFloat(reader, code)
case code >= 0xcc && code <= 0xd3:
return decodeMessagePackInteger(reader, code)
case code == 0xd9 || code == 0xda || code == 0xdb:
length, err := decodeLength(reader, code, 0xd9)
if err != nil {
return nil, err
}
return decodeMessagePackString(reader, length)
case code == 0xdc || code == 0xdd:
length, err := decodeLength(reader, code, 0xdc)
if err != nil {
return nil, err
}
return decodeMessagePackArray(reader, length, depth)
case code == 0xde || code == 0xdf:
length, err := decodeLength(reader, code, 0xde)
if err != nil {
return nil, err
}
return decodeMessagePackMap(reader, length, depth)
default:
return nil, fmt.Errorf("unsupported MessagePack type 0x%02x", code)
}
}
func decodeLength(reader *bytes.Reader, code, base byte) (int, error) {
var size int
switch code - base {
case 0:
size = 1
case 1:
size = 2
case 2:
size = 4
default:
return 0, fmt.Errorf("invalid length type 0x%02x", code)
}
buffer := make([]byte, size)
if _, err := io.ReadFull(reader, buffer); err != nil {
return 0, err
}
value := uint64(0)
for _, octet := range buffer {
value = value<<8 | uint64(octet)
}
if value > uint64(maxPayload) {
return 0, errors.New("MessagePack value exceeds 64 KiB")
}
return int(value), nil
}
func decodeMessagePackString(reader *bytes.Reader, length int) (string, error) {
value := make([]byte, length)
if _, err := io.ReadFull(reader, value); err != nil {
return "", err
}
return string(value), nil
}
func decodeMessagePackFloat(reader *bytes.Reader, code byte) (any, error) {
var buffer []byte
if code == 0xca {
buffer = make([]byte, 4)
} else {
buffer = make([]byte, 8)
}
if _, err := io.ReadFull(reader, buffer); err != nil {
return nil, err
}
if code == 0xca {
bits := binary.BigEndian.Uint32(buffer)
return float64(math.Float32frombits(bits)), nil
}
return math.Float64frombits(binary.BigEndian.Uint64(buffer)), nil
}
func decodeMessagePackInteger(reader *bytes.Reader, code byte) (any, error) {
switch code {
case 0xcc, 0xd0:
buffer := make([]byte, 1)
if _, err := io.ReadFull(reader, buffer); err != nil {
return nil, err
}
if code == 0xcc {
return uint64(buffer[0]), nil
}
return int64(int8(buffer[0])), nil
case 0xcd, 0xd1:
buffer := make([]byte, 2)
if _, err := io.ReadFull(reader, buffer); err != nil {
return nil, err
}
value := binary.BigEndian.Uint16(buffer)
if code == 0xcd {
return uint64(value), nil
}
return int64(int16(value)), nil
case 0xce, 0xd2:
buffer := make([]byte, 4)
if _, err := io.ReadFull(reader, buffer); err != nil {
return nil, err
}
value := binary.BigEndian.Uint32(buffer)
if code == 0xce {
return uint64(value), nil
}
return int64(int32(value)), nil
case 0xcf, 0xd3:
buffer := make([]byte, 8)
if _, err := io.ReadFull(reader, buffer); err != nil {
return nil, err
}
value := binary.BigEndian.Uint64(buffer)
if code == 0xcf {
return value, nil
}
return int64(value), nil
default:
return nil, fmt.Errorf("invalid integer type 0x%02x", code)
}
}
func decodeMessagePackArray(reader *bytes.Reader, length, depth int) ([]any, error) {
values := make([]any, length)
for index := range values {
value, err := decodeMessagePackDepth(reader, depth+1)
if err != nil {
return nil, err
}
values[index] = value
}
return values, nil
}
func decodeMessagePackMap(reader *bytes.Reader, length, depth int) (map[any]any, error) {
values := make(map[any]any, length)
for range length {
key, err := decodeMessagePackDepth(reader, depth+1)
if err != nil {
return nil, err
}
switch key.(type) {
case string, messagePackBinary:
default:
return nil, errors.New("MessagePack map key must be text")
}
value, err := decodeMessagePackDepth(reader, depth+1)
if err != nil {
return nil, err
}
values[key] = value
}
return values, nil
}