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"}
|
errNilStoreOperands = &Error{message: "vmStore: src and/or dst operands are nil"}
|
||||||
errInvalidStoreDestination = &Error{message: "vmStore: destination operand is not an AML entity"}
|
errInvalidStoreDestination = &Error{message: "vmStore: destination operand is not an AML entity"}
|
||||||
errCopyFailed = &Error{message: "vmCopyObject: copy failed"}
|
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.
|
// objRef is a pointer to an argument (local or global) or a named AML object.
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package aml
|
package aml
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
// valueType represents the data types that the AML interpreter can process.
|
// valueType represents the data types that the AML interpreter can process.
|
||||||
type valueType uint8
|
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.
|
// vmTypeOf returns the type of data stored inside the supplied argument.
|
||||||
func vmTypeOf(ctx *execContext, arg interface{}) valueType {
|
func vmTypeOf(ctx *execContext, arg interface{}) valueType {
|
||||||
// Some objects (e.g args, constEntity contents) may require to perform
|
// Some objects (e.g args, constEntity contents) may require to perform
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package aml
|
package aml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"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