mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
acpi: implement string <=> int converter
The converter is pretty basic at the moment and it only supports converting to/from Integer and String types. This commit also includes some argument => uint64 conversion helpers that will serve as the basis for implementing the ALU opcodes.
This commit is contained in:
parent
d6a825fc02
commit
a172621af7
@ -16,6 +16,8 @@ var (
|
||||
errNilStoreOperands = &Error{message: "vmStore: src and/or dst operands are nil"}
|
||||
errInvalidStoreDestination = &Error{message: "vmStore: destination operand is not an AML entity"}
|
||||
errCopyFailed = &Error{message: "vmCopyObject: copy failed"}
|
||||
errConversionFailed = &Error{message: "vmConvert: conversion failed"}
|
||||
errArgIndexOutOfBounds = &Error{message: "vm: arg index out of bounds"}
|
||||
)
|
||||
|
||||
// objRef is a pointer to an argument (local or global) or a named AML object.
|
||||
|
@ -1,5 +1,7 @@
|
||||
package aml
|
||||
|
||||
import "strconv"
|
||||
|
||||
// valueType represents the data types that the AML interpreter can process.
|
||||
type valueType uint8
|
||||
|
||||
@ -70,6 +72,105 @@ func (vt valueType) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// vmToIntArg attempts to convert the entity argument at position argIndex to
|
||||
// a uint64 value.
|
||||
func vmToIntArg(ctx *execContext, ent Entity, argIndex int) (uint64, *Error) {
|
||||
args := ent.getArgs()
|
||||
if len(args) <= argIndex {
|
||||
return 0, errArgIndexOutOfBounds
|
||||
}
|
||||
|
||||
argVal, err := vmConvert(ctx, args[argIndex], valueTypeInteger)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return argVal.(uint64), nil
|
||||
}
|
||||
|
||||
// vmToIntArgs2 attempts to convert the entity arguments at positions argIndex1
|
||||
// and argIndex2 to uint64 values.
|
||||
func vmToIntArgs2(ctx *execContext, ent Entity, argIndex1, argIndex2 int) (uint64, uint64, *Error) {
|
||||
args := ent.getArgs()
|
||||
if len(args) <= argIndex1 || len(args) <= argIndex2 {
|
||||
return 0, 0, errArgIndexOutOfBounds
|
||||
}
|
||||
|
||||
argVal1, err := vmConvert(ctx, args[argIndex1], valueTypeInteger)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
argVal2, err := vmConvert(ctx, args[argIndex2], valueTypeInteger)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return argVal1.(uint64), argVal2.(uint64), nil
|
||||
|
||||
}
|
||||
|
||||
// vmConvert attempts to convert the input argument to the specified type. If
|
||||
// the conversion is not possible then vmConvert returns back an error.
|
||||
func vmConvert(ctx *execContext, arg interface{}, toType valueType) (interface{}, *Error) {
|
||||
argVal, err := vmLoad(ctx, arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Conversion not required; we can just read the value directly
|
||||
argType := vmTypeOf(ctx, argVal)
|
||||
if argType == toType {
|
||||
return argVal, nil
|
||||
}
|
||||
|
||||
switch argType {
|
||||
case valueTypeString:
|
||||
argAsStr := argVal.(string)
|
||||
switch toType {
|
||||
case valueTypeInteger:
|
||||
// According to the spec: If no integer object exists, a new integer is created. The
|
||||
// integer is initialized to the value zero and the ASCII string is interpreted as a
|
||||
// hexadecimal constant. Each string character is interpreted as a hexadecimal value
|
||||
// (‘0’- ‘9’, ‘A’-‘F’, ‘a’-‘f’), starting with the first character as the most significant
|
||||
// digit, and ending with the first non-hexadecimal character, end-of-string, or
|
||||
// when the size of an integer is reached (8 characters for 32-bit integers and 16
|
||||
// characters for 64-bit integers). Note: the first non-hex character terminates the
|
||||
// conversion without error, and a “0x” prefix is not allowed. Conversion of a null
|
||||
// (zero-length) string to an integer is not allowed.
|
||||
if len(argAsStr) == 0 {
|
||||
return nil, errConversionFailed
|
||||
}
|
||||
|
||||
var res = uint64(0)
|
||||
for i := 0; i < len(argAsStr) && i < ctx.vm.sizeOfIntInBits>>2; i++ {
|
||||
ch := argAsStr[i]
|
||||
if ch >= '0' && ch <= '9' {
|
||||
res = res<<4 | uint64(ch-'0')
|
||||
} else if ch >= 'a' && ch <= 'f' {
|
||||
res = res<<4 | uint64(ch-'a'+10)
|
||||
} else if ch >= 'A' && ch <= 'F' {
|
||||
res = res<<4 | uint64(ch-'A'+10)
|
||||
} else {
|
||||
// non-hex character; we should stop and return without an error
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
case valueTypeInteger:
|
||||
argAsInt := argVal.(uint64)
|
||||
switch toType {
|
||||
case valueTypeString:
|
||||
// Integers are formatted as hex strings without a 0x prefix
|
||||
return strconv.FormatUint(argAsInt, 16), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errConversionFailed
|
||||
}
|
||||
|
||||
// vmTypeOf returns the type of data stored inside the supplied argument.
|
||||
func vmTypeOf(ctx *execContext, arg interface{}) valueType {
|
||||
// Some objects (e.g args, constEntity contents) may require to perform
|
||||
|
@ -1,6 +1,7 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -172,3 +173,208 @@ func TestVMTypeOf(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMToIntArg(t *testing.T) {
|
||||
ctx := &execContext{
|
||||
vm: &VM{sizeOfIntInBits: 64},
|
||||
}
|
||||
|
||||
specs := []struct {
|
||||
ent Entity
|
||||
argIndex int
|
||||
expVal uint64
|
||||
expErr *Error
|
||||
}{
|
||||
{
|
||||
&unnamedEntity{
|
||||
args: []interface{}{uint64(42)},
|
||||
},
|
||||
0,
|
||||
42,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
&unnamedEntity{
|
||||
args: []interface{}{""},
|
||||
},
|
||||
0,
|
||||
0,
|
||||
errConversionFailed,
|
||||
},
|
||||
{
|
||||
&unnamedEntity{},
|
||||
0,
|
||||
0,
|
||||
errArgIndexOutOfBounds,
|
||||
},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
got, err := vmToIntArg(ctx, spec.ent, spec.argIndex)
|
||||
switch {
|
||||
case !reflect.DeepEqual(spec.expErr, err):
|
||||
t.Errorf("[spec %d] expected error: %v; got: %v", specIndex, spec.expErr, err)
|
||||
case got != spec.expVal:
|
||||
t.Errorf("[spec %d] expected to get value %v; got %v", specIndex, spec.expVal, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMToIntArgs2(t *testing.T) {
|
||||
ctx := &execContext{
|
||||
vm: &VM{sizeOfIntInBits: 64},
|
||||
}
|
||||
|
||||
specs := []struct {
|
||||
ent Entity
|
||||
argIndex [2]int
|
||||
expVal [2]uint64
|
||||
expErr *Error
|
||||
}{
|
||||
{
|
||||
&unnamedEntity{
|
||||
args: []interface{}{uint64(42), uint64(999)},
|
||||
},
|
||||
[2]int{0, 1},
|
||||
[2]uint64{42, 999},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
&unnamedEntity{
|
||||
args: []interface{}{"", uint64(999)},
|
||||
},
|
||||
[2]int{0, 1},
|
||||
[2]uint64{0, 0},
|
||||
errConversionFailed,
|
||||
},
|
||||
{
|
||||
&unnamedEntity{
|
||||
args: []interface{}{uint64(123), ""},
|
||||
},
|
||||
[2]int{0, 1},
|
||||
[2]uint64{0, 0},
|
||||
errConversionFailed,
|
||||
},
|
||||
{
|
||||
&unnamedEntity{},
|
||||
[2]int{128, 0},
|
||||
[2]uint64{0, 0},
|
||||
errArgIndexOutOfBounds,
|
||||
},
|
||||
{
|
||||
&unnamedEntity{args: []interface{}{uint64(42)}},
|
||||
[2]int{0, 128},
|
||||
[2]uint64{0, 0},
|
||||
errArgIndexOutOfBounds,
|
||||
},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
got1, got2, err := vmToIntArgs2(ctx, spec.ent, 0, 1)
|
||||
switch {
|
||||
case !reflect.DeepEqual(spec.expErr, err):
|
||||
t.Errorf("[spec %d] expected error: %v; got: %v", specIndex, spec.expErr, err)
|
||||
case got1 != spec.expVal[0] || got2 != spec.expVal[1]:
|
||||
t.Errorf("[spec %d] expected to get values [%v, %v] ; got [%v, %v]", specIndex,
|
||||
spec.expVal[0], spec.expVal[1],
|
||||
got1, got2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMConvert(t *testing.T) {
|
||||
specs := []struct {
|
||||
ctx *execContext
|
||||
in interface{}
|
||||
toType valueType
|
||||
expVal interface{}
|
||||
expErr *Error
|
||||
}{
|
||||
// No conversion required
|
||||
{
|
||||
nil,
|
||||
"foo",
|
||||
valueTypeString,
|
||||
"foo",
|
||||
nil,
|
||||
},
|
||||
// string -> int (32-bit mode)
|
||||
{
|
||||
&execContext{
|
||||
vm: &VM{sizeOfIntInBits: 32},
|
||||
},
|
||||
"bAdF00D9",
|
||||
valueTypeInteger,
|
||||
uint64(0xbadf00d9),
|
||||
nil,
|
||||
},
|
||||
// string -> int (64-bit mode)
|
||||
{
|
||||
&execContext{
|
||||
vm: &VM{sizeOfIntInBits: 64},
|
||||
},
|
||||
"feedfaceDEADC0DE-ignored-data",
|
||||
valueTypeInteger,
|
||||
uint64(0xfeedfacedeadc0de),
|
||||
nil,
|
||||
},
|
||||
// string -> int (64-bit mode) ; stop at first non-hex char
|
||||
{
|
||||
&execContext{
|
||||
vm: &VM{sizeOfIntInBits: 64},
|
||||
},
|
||||
"feedGARBAGE",
|
||||
valueTypeInteger,
|
||||
uint64(0xfeed),
|
||||
nil,
|
||||
},
|
||||
// string -> int; empty string should trigger an error
|
||||
{
|
||||
&execContext{
|
||||
vm: &VM{sizeOfIntInBits: 64},
|
||||
},
|
||||
"",
|
||||
valueTypeInteger,
|
||||
nil,
|
||||
errConversionFailed,
|
||||
},
|
||||
// int -> string
|
||||
{
|
||||
nil,
|
||||
uint64(0xfeedfacedeadc0de),
|
||||
valueTypeString,
|
||||
"feedfacedeadc0de",
|
||||
nil,
|
||||
},
|
||||
// vmLoad returns an error
|
||||
{
|
||||
nil,
|
||||
&unnamedEntity{op: opAdd},
|
||||
valueTypeInteger,
|
||||
nil,
|
||||
&Error{message: "readArg: unsupported entity type: " + opAdd.String()},
|
||||
},
|
||||
// unsupported conversion
|
||||
{
|
||||
nil,
|
||||
uint64(42),
|
||||
valueTypeDevice,
|
||||
nil,
|
||||
errConversionFailed,
|
||||
},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
got, err := vmConvert(spec.ctx, spec.in, spec.toType)
|
||||
switch {
|
||||
case !reflect.DeepEqual(spec.expErr, err):
|
||||
t.Errorf("[spec %d] expected error: %v; got: %v", specIndex, spec.expErr, err)
|
||||
case got != spec.expVal:
|
||||
t.Errorf("[spec %d] expected to get value %v (type: %v); got %v (type %v)", specIndex,
|
||||
spec.expVal, reflect.TypeOf(spec.expVal),
|
||||
got, reflect.TypeOf(got),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user