/
xml.go
166 lines (152 loc) · 3.52 KB
/
xml.go
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package blob
import (
"bytes"
"encoding/binary"
"encoding/xml"
"fmt"
"io"
"strings"
"unicode/utf16"
"unicode/utf8"
)
const (
BlockListTag = "BlockList"
BlockLatest = "Latest"
BlockCommitted = "Committed"
BlockUncommitted = "Uncommitted"
BlockAll = "All"
)
type XBlockIn struct {
ID string
Mode string
}
type XBlockListIn struct {
Blocks []XBlockIn
}
func (b *XBlockIn) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
name := start.Name.Local
if name != BlockLatest && name != BlockCommitted && name != BlockUncommitted {
return fmt.Errorf("unexpected block tag: %v", start.Name.Local)
}
b.Mode = name
for {
t, err := d.Token()
if err != nil {
return err
}
switch t2 := t.(type) {
case xml.CharData:
b.ID = strings.TrimSpace(string(t2))
case xml.EndElement:
if t2.Name.Local == name {
return nil
}
return fmt.Errorf("unexpected closing block tag: %v", t2.Name.Local)
case xml.ProcInst:
case xml.Comment:
case xml.Directive:
default:
return fmt.Errorf("unexpected token: %v", t)
}
}
}
func (b *XBlockIn) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
var err error
err = e.EncodeToken(xml.StartElement{Name: xml.Name{Local: b.Mode}})
if err != nil {
return err
}
err = e.EncodeToken(xml.CharData(b.ID))
if err != nil {
return err
}
err = e.EncodeToken(xml.EndElement{Name: xml.Name{Local: b.Mode}})
return err
}
func (bl *XBlockListIn) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if start.Name.Local != BlockListTag {
return fmt.Errorf("unexpected block list tag: %v", start.Name.Local)
}
for {
t, err := d.Token()
if err != nil {
return err
}
switch t2 := t.(type) {
case xml.StartElement:
var b XBlockIn
err = d.DecodeElement(&b, &t2)
if err != nil {
return err
}
bl.Blocks = append(bl.Blocks, b)
case xml.EndElement:
if t2.Name.Local == BlockListTag {
return nil
}
return fmt.Errorf("unexpected closing blocklist tag: %v", t2.Name.Local)
case xml.CharData:
case xml.ProcInst:
case xml.Comment:
case xml.Directive:
default:
return fmt.Errorf("unexpected token: %v", t)
}
}
}
func (bl *XBlockListIn) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
var err error
err = e.EncodeToken(xml.StartElement{Name: xml.Name{Local: BlockListTag}})
if err != nil {
return err
}
for _, b := range bl.Blocks {
err = e.Encode(b)
if err != nil {
return err
}
}
err = e.EncodeToken(xml.EndElement{Name: xml.Name{Local: BlockListTag}})
return err
}
func ParseBlocklistXML(data []byte) (*XBlockListIn, error) {
bl := &XBlockListIn{}
data = utf16utf8(data, binary.LittleEndian)
d := xml.NewDecoder(bytes.NewBuffer(data))
d.CharsetReader = func(s string, r io.Reader) (io.Reader, error) {
return r, nil
}
err := d.Decode(bl)
if err != nil {
return nil, err
}
return bl, nil
}
// 21century, we can't convert charset in golang. nice
func utf16utf8(b []byte, o binary.ByteOrder) []byte {
utf := make([]uint16, (len(b)+(2-1))/2)
for i := 0; i+(2-1) < len(b); i += 2 {
utf[i/2] = o.Uint16(b[i:])
}
if len(b)/2 < len(utf) {
utf[len(utf)-1] = utf8.RuneError
}
return []byte(string(utf16.Decode(utf)))
}
type XBlockListOut struct {
XMLName xml.Name `xml:"BlockList"`
CommittedBlocks struct {
Blocks []XBlockOut
}
UncommittedBlocks struct {
Blocks []XBlockOut
}
}
type XBlockOut struct {
XMLName xml.Name `xml:"Block"`
Name string
Size uint64
}
func SerializeBlocklistXML(bl *XBlockListOut) ([]byte, error) {
return xml.Marshal(bl)
}