-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvalue.go
More file actions
93 lines (86 loc) · 2.45 KB
/
value.go
File metadata and controls
93 lines (86 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package mdb
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"math"
"time"
)
func decodeValue(file io.ReadSeeker, kind dataType, buff []byte) (val interface{}, err error) {
switch kind {
case typeBool:
return buff[0] != 0, nil
case typeByte:
return buff[0], nil
case typeInt:
return int(int16(binary.LittleEndian.Uint16(buff[:2]))), nil
case typeLongInt:
return int(int32(binary.LittleEndian.Uint32(buff[:4]))), nil
case typeMoney:
return nil, fmt.Errorf("data type %s is not implemented", kind)
case typeFloat:
return math.Float32frombits(binary.LittleEndian.Uint32(buff[:4])), nil
case typeDouble:
return math.Float64frombits(binary.LittleEndian.Uint64(buff[:8])), nil
case typeDateTime:
return decodeDateTime(buff), nil
case typeBinary:
return buff, nil
case typeText:
return string(buff), nil
case typeLongBinary:
return decodeLong(file, buff)
case typeLongText:
buff, err = decodeLong(file, buff)
return string(buff), err
case typeUNKNOWN_0D:
return nil, fmt.Errorf("data type %s is not implemented", kind)
case typeUNKNOWN_0E:
return nil, fmt.Errorf("data type %s is not implemented", kind)
case typeGUID:
return nil, fmt.Errorf("data type %s is not implemented", kind)
case typeNumeric:
return nil, fmt.Errorf("data type %s is not implemented", kind)
default:
return nil, fmt.Errorf("invalid data type: %s", kind)
}
}
func decodeLong(file io.ReadSeeker, buff []byte) ([]byte, error) {
head := int(binary.LittleEndian.Uint32(buff[:4]))
size := head & 0x00ffffff
switch buff[3] {
case 0x80: // inline
return buff[12 : 12+size], nil
case 0x40: // single page
return findRowWithPtr(file, int(binary.LittleEndian.Uint32(buff[4:8])))
case 0x00:
// below
default:
return nil, fmt.Errorf("invalid long data header 0x%02x", buff[3])
}
var out bytes.Buffer
ptr := int(binary.LittleEndian.Uint32(buff[4:8]))
for ptr != 0 {
data, err := findRowWithPtr(file, ptr)
if err != nil {
return nil, err
}
if out.Len()+len(data)-4 > head {
break
}
if size == 0 {
break
}
out.Write(data[4:])
ptr = int(binary.LittleEndian.Uint32(data[:4]))
}
if out.Len() != head {
return nil, fmt.Errorf("incorrect data length")
}
return out.Bytes(), nil
}
func decodeDateTime(buff []byte) time.Time {
d := math.Float64frombits(binary.LittleEndian.Uint64(buff))
return time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC).Add(time.Duration(d)*time.Hour*24 + time.Second*time.Duration(math.Abs(d-float64(int(d)))*86400+0.5))
}