Skip to content

Commit f2079e4

Browse files
committed
Refactor writer into encoder
1 parent f22a70d commit f2079e4

File tree

3 files changed

+47
-85
lines changed

3 files changed

+47
-85
lines changed

command/ota/generate.go

+5-7
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
package ota
1919

2020
import (
21-
"bytes"
2221
"errors"
22+
"fmt"
2323
"io/ioutil"
2424
"os"
2525

@@ -51,17 +51,15 @@ func Generate(binFile string, outFile string, fqbn string) error {
5151
return err
5252
}
5353

54-
var w bytes.Buffer
55-
otaWriter := inota.NewWriter(&w, arduinoVendorID, productID)
56-
_, err = otaWriter.Write(data)
54+
out, err := os.Create(outFile)
5755
if err != nil {
5856
return err
5957
}
60-
otaWriter.Close()
6158

62-
err = ioutil.WriteFile(outFile, w.Bytes(), os.FileMode(0644))
59+
enc := inota.NewEncoder(out, arduinoVendorID, productID)
60+
err = enc.Encode(data)
6361
if err != nil {
64-
return err
62+
return fmt.Errorf("failed to encode binary file: %w", err)
6563
}
6664

6765
return nil

internal/ota/encoder.go

+36-66
Original file line numberDiff line numberDiff line change
@@ -18,60 +18,48 @@
1818
package ota
1919

2020
import (
21-
"bufio"
2221
"encoding/binary"
22+
"fmt"
2323
"hash/crc32"
2424
"io"
2525
"strconv"
2626

2727
"github.com/arduino/arduino-cloud-cli/internal/lzss"
28-
"github.com/juju/errors"
2928
)
3029

31-
// A writer is a buffered, flushable writer.
32-
type writer interface {
33-
io.Writer
34-
Flush() error
35-
}
36-
37-
// encoder encodes a binary into an .ota file.
38-
type encoder struct {
39-
// w is the writer that compressed bytes are written to.
40-
w writer
30+
// Encoder writes a binary to an output stream in the ota format.
31+
type Encoder struct {
32+
// w is the stream where encoded bytes are written.
33+
w io.Writer
4134

42-
// vendorID is the ID of the board vendor
35+
// vendorID is the ID of the board vendor.
4336
vendorID string
4437

45-
// is the ID of the board vendor is the ID of the board model
38+
// productID is the ID of the board model.
4639
productID string
4740
}
4841

49-
// NewWriter creates a new `WriteCloser` for the the given VID/PID.
50-
func NewWriter(w io.Writer, vendorID, productID string) io.WriteCloser {
51-
bw, ok := w.(writer)
52-
if !ok {
53-
bw = bufio.NewWriter(w)
54-
}
55-
return &encoder{
56-
w: bw,
42+
// NewEncoder creates a new ota encoder.
43+
func NewEncoder(w io.Writer, vendorID, productID string) *Encoder {
44+
return &Encoder{
45+
w: w,
5746
vendorID: vendorID,
5847
productID: productID,
5948
}
6049
}
6150

62-
// Write writes a compressed representation of p to e's underlying writer.
63-
func (e *encoder) Write(binaryData []byte) (int, error) {
64-
//log.Println("original binaryData is", len(binaryData), "bytes length")
65-
66-
// Magic number (VID/PID)
51+
// Encode compresses data using a lzss algorithm, encodes the result
52+
// in ota format and writes it to e's underlying writer.
53+
func (e *Encoder) Encode(data []byte) error {
54+
// Compute the magic number (VID/PID)
6755
magicNumber := make([]byte, 4)
6856
vid, err := strconv.ParseUint(e.vendorID, 16, 16)
6957
if err != nil {
70-
return 0, errors.Annotate(err, "OTA encoder: failed to parse vendorID")
58+
return fmt.Errorf("cannot parse vendorID: %w", err)
7159
}
7260
pid, err := strconv.ParseUint(e.productID, 16, 16)
7361
if err != nil {
74-
return 0, errors.Annotate(err, "OTA encoder: failed to parse productID")
62+
return fmt.Errorf("cannot parse productID: %w", err)
7563
}
7664

7765
binary.LittleEndian.PutUint16(magicNumber[0:2], uint16(pid))
@@ -82,61 +70,43 @@ func (e *encoder) Write(binaryData []byte) (int, error) {
8270
Compression: true,
8371
}
8472

85-
// Compress the compiled binary
86-
compressed := lzss.Encode(binaryData)
87-
73+
compressed := lzss.Encode(data)
8874
// Prepend magic number and version field to payload
89-
var binDataComplete []byte
90-
binDataComplete = append(binDataComplete, magicNumber...)
91-
binDataComplete = append(binDataComplete, version.AsBytes()...)
92-
binDataComplete = append(binDataComplete, compressed...)
93-
//log.Println("binDataComplete is", len(binDataComplete), "bytes length")
75+
var outData []byte
76+
outData = append(outData, magicNumber...)
77+
outData = append(outData, version.Bytes()...)
78+
outData = append(outData, compressed...)
9479

95-
headerSize, err := e.writeHeader(binDataComplete)
80+
err = e.writeHeader(outData)
9681
if err != nil {
97-
return headerSize, err
82+
return fmt.Errorf("cannot write data header to output stream: %w", err)
9883
}
9984

100-
payloadSize, err := e.writePayload(binDataComplete)
85+
_, err = e.w.Write(outData)
10186
if err != nil {
102-
return payloadSize, err
87+
return fmt.Errorf("cannot write encoded data to output stream: %w", err)
10388
}
10489

105-
return headerSize + payloadSize, nil
106-
}
107-
108-
// Close closes the encoder, flushing any pending output. It does not close or
109-
// flush e's underlying writer.
110-
func (e *encoder) Close() error {
111-
return e.w.Flush()
90+
return nil
11291
}
11392

114-
func (e *encoder) writeHeader(binDataComplete []byte) (int, error) {
115-
93+
func (e *Encoder) writeHeader(data []byte) error {
11694
// Write the length of the content
11795
lengthAsBytes := make([]byte, 4)
118-
binary.LittleEndian.PutUint32(lengthAsBytes, uint32(len(binDataComplete)))
119-
120-
n, err := e.w.Write(lengthAsBytes)
96+
binary.LittleEndian.PutUint32(lengthAsBytes, uint32(len(data)))
97+
_, err := e.w.Write(lengthAsBytes)
12198
if err != nil {
122-
return n, err
99+
return err
123100
}
124101

125-
// Calculate the checksum for binDataComplete
126-
crc := crc32.ChecksumIEEE(binDataComplete)
127-
128-
// encode the checksum uint32 value as 4 bytes
102+
// Write the checksum uint32 value as 4 bytes
103+
crc := crc32.ChecksumIEEE(data)
129104
crcAsBytes := make([]byte, 4)
130105
binary.LittleEndian.PutUint32(crcAsBytes, crc)
131-
132-
n, err = e.w.Write(crcAsBytes)
106+
_, err = e.w.Write(crcAsBytes)
133107
if err != nil {
134-
return n, err
108+
return err
135109
}
136110

137-
return len(lengthAsBytes) + len(crcAsBytes), nil
138-
}
139-
140-
func (e *encoder) writePayload(data []byte) (int, error) {
141-
return e.w.Write(data)
111+
return nil
142112
}

internal/ota/encoder_test.go

+6-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"bytes"
2222
"encoding/hex"
2323
"io/ioutil"
24-
"log"
2524

2625
"fmt"
2726
"hash/crc32"
@@ -38,25 +37,21 @@ func TestComputeCrc32Checksum(t *testing.T) {
3837
assert.Equal(t, crc, uint32(2090640218))
3938
}
4039

41-
func TestEncoderWrite(t *testing.T) {
40+
func TestEncode(t *testing.T) {
4241
// Setup test data
4342
data, _ := hex.DecodeString("DEADBEEF") // uncompressed, or 'ef 6b 77 de f0' (compressed w/ LZSS)
4443

4544
var w bytes.Buffer
4645
vendorID := "2341" // Arduino
4746
productID := "8054" // MRK Wifi 1010
4847

49-
otaWriter := NewWriter(&w, vendorID, productID)
50-
otaWriter.Close()
48+
enc := NewEncoder(&w, vendorID, productID)
5149

52-
n, err := otaWriter.Write(data)
50+
err := enc.Encode(data)
5351
if err != nil {
5452
t.Error(err)
55-
t.Fail()
5653
}
57-
log.Println("written ota of", n, "bytes length")
5854

59-
otaWriter.Close()
6055
actual := w.Bytes()
6156

6257
// You can get the expected result creating an `.ota` file using Alex's tools:
@@ -73,7 +68,7 @@ func TestEncoderWrite(t *testing.T) {
7368
assert.Assert(t, res == 0) // 0 means equal
7469
}
7570

76-
func TestEncoderWriteFiles(t *testing.T) {
71+
func TestEncodeFiles(t *testing.T) {
7772
tests := []struct {
7873
name string
7974
infile string
@@ -106,12 +101,11 @@ func TestEncoderWriteFiles(t *testing.T) {
106101
var got bytes.Buffer
107102
vendorID := "2341" // Arduino
108103
productID := "8057" // Nano 33 IoT
109-
otaWriter := NewWriter(&got, vendorID, productID)
110-
_, err = otaWriter.Write(input)
104+
otaenc := NewEncoder(&got, vendorID, productID)
105+
err = otaenc.Encode(input)
111106
if err != nil {
112107
t.Error(err)
113108
}
114-
otaWriter.Close()
115109

116110
if !bytes.Equal(want, got.Bytes()) {
117111
t.Error("encoding failed")

0 commit comments

Comments
 (0)