forked from TheThingsArchive/ttn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
readwriter.go
150 lines (136 loc) · 4 KB
/
readwriter.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
// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.
package readwriter
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"github.com/TheThingsNetwork/ttn/utils/errors"
"github.com/brocaar/lorawan"
)
type Interface interface {
Write(data interface{})
TryRead(to func(data []byte) error)
Read(to func(data []byte))
Bytes() ([]byte, error)
Err() error
}
// entryReadWriter offers convenient method to write and read successively from a bytes buffer.
type rw struct {
err error
data *bytes.Buffer
}
// newEntryReadWriter create a new read/writer from an existing buffer.
//
// If a nil or empty buffer is supplied, reading from the read/writer will cause an error (io.EOF)
// Nevertheless, if a valid non-empty buffer is given, the read/writer will start reading from the
// beginning of that buffer, and will start writting at the end of it.
func New(buf []byte) Interface {
return &rw{
err: nil,
data: bytes.NewBuffer(buf),
}
}
// Write appends the given data at the end of the existing buffer.
//
// It does nothing if an error was previously noticed and panics if the given data are something
// different from: byte, []byte, string, AES128Key, EUI64, DevAddr.
//
// Also, it writes the length of the given raw data encoded on 2 bytes before writting the data
// itself. In that way, data can be appended and read easily.
func (w *rw) Write(data interface{}) {
var dataLen uint16
switch data.(type) {
case bool:
if data.(bool) {
w.directWrite(uint16(1))
w.directWrite(byte(1))
} else {
w.directWrite(uint16(1))
w.directWrite(byte(0))
}
return
case uint8:
dataLen = 1
case uint16:
dataLen = 2
case uint32:
dataLen = 4
case uint64:
dataLen = 8
case []byte:
dataLen = uint16(len(data.([]byte)))
case lorawan.AES128Key:
dataLen = uint16(len(data.(lorawan.AES128Key)))
case lorawan.EUI64:
dataLen = uint16(len(data.(lorawan.EUI64)))
case lorawan.DevAddr:
dataLen = uint16(len(data.(lorawan.DevAddr)))
case string:
str := data.(string)
w.directWrite(uint16(len(str)))
w.directWrite([]byte(str))
return
default:
panic(fmt.Errorf("Unreckognized data type: %v", data))
}
w.directWrite(dataLen)
w.directWrite(data)
}
// directWrite appends the given data at the end of the existing buffer (without the length).
func (w *rw) directWrite(data interface{}) {
if w.err != nil {
return
}
in := w.data.Next(w.data.Len())
w.data = new(bytes.Buffer)
binary.Write(w.data, binary.BigEndian, in)
w.err = binary.Write(w.data, binary.BigEndian, data)
}
// Read retrieves next data from the given buffer. Implicitely, this implies the data to have been
// written using the Write method (len | data). Data are sent back through a callback as an array of
// bytes.
func (w *rw) Read(to func(data []byte)) {
w.err = w.read(func(data []byte) error { to(data); return nil })
}
// TryRead retrieves the next data from the given buffer but differs from Read in the way it listen
// to the callback returns for a possible error.
func (w *rw) TryRead(to func(data []byte) error) {
w.err = w.read(to)
}
func (w *rw) read(to func(data []byte) error) error {
if w.err != nil {
return w.err
}
lenTo := new(uint16)
if err := binary.Read(w.data, binary.BigEndian, lenTo); err != nil {
return err
}
next := w.data.Next(int(*lenTo))
if len(next) != int(*lenTo) {
return errors.New(errors.Structural, "Not enough data to read")
}
return to(next)
}
// Bytes might be used to retrieves the raw buffer after successive writes. It will return nil and
// an error if any issue was encountered during the process.
func (w rw) Bytes() ([]byte, error) {
if w.err != nil {
return nil, w.Err()
}
return w.data.Bytes(), nil
}
// Err just return the err status of the read-writer.
func (w rw) Err() error {
if w.err != nil {
if w.err == io.EOF {
return errors.New(errors.Behavioural, w.err)
}
if failure, ok := w.err.(errors.Failure); ok {
return failure
}
return errors.New(errors.Operational, w.err)
}
return nil
}