78 lines
2.7 KiB
Go
78 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
)
|
|
|
|
// Documents cross the bus in parts (novox/hq ADR 0468 clause 3): the bus refuses a message over 1 MiB, and a
|
|
// part travels base64-encoded inside a JSON message.
|
|
const (
|
|
// PartSize is how large the parts this module cuts are, and MaxPart the most raw bytes a part may hold; a part
|
|
// of more is refused. They are one: 700 KiB, so that a part's base64 text (about 933 KB) and the message around
|
|
// it stay under the bus's 1 MiB. (ADR 0468 clause 3.)
|
|
PartSize = 700 << 10
|
|
MaxPart = PartSize
|
|
// AnswerPartSize is the raw size of a part the device process answers in the state device-answers
|
|
// (scanner.document). The state refuses a value over 256 KiB (the mesh's cap on every bucket's value), and a
|
|
// part travels base64-encoded in JSON, so 128 KiB (about 175 KB written) fits with room to spare; a part of
|
|
// 700 KiB (about 933 KB) can only cross the bus as a message, never as a state value (issue 1535).
|
|
AnswerPartSize = 128 << 10
|
|
// StateMaxValue is the most a state value may hold.
|
|
StateMaxValue = 256 << 10
|
|
// MostAnswerParts bounds how many answer parts a document has (a 100 MiB document is the most).
|
|
MostAnswerParts = 800
|
|
// MostParts bounds how many parts one document has (a 100 MiB document is the most).
|
|
MostParts = 150
|
|
)
|
|
|
|
func sha256Hex(b []byte) string {
|
|
sum := sha256.Sum256(b)
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
// partCount is how many parts of PartSize hold size bytes: at least one.
|
|
func partCount(size int) int { return partsOf(size, PartSize) }
|
|
|
|
// answerPartCount is how many parts of AnswerPartSize hold size bytes: at least one.
|
|
func answerPartCount(size int) int { return partsOf(size, AnswerPartSize) }
|
|
|
|
func partsOf(size, each int) int {
|
|
if size <= 0 {
|
|
return 1
|
|
}
|
|
return (size + each - 1) / each
|
|
}
|
|
|
|
// cutPart is part number part (counted from 1) of data, in parts of AnswerPartSize, and how many parts there are.
|
|
func cutPart(data []byte, part int) ([]byte, int, error) {
|
|
return cutPartOf(data, part, AnswerPartSize)
|
|
}
|
|
|
|
func cutPartOf(data []byte, part, each int) ([]byte, int, error) {
|
|
n := partsOf(len(data), each)
|
|
if part < 1 || part > n {
|
|
return nil, n, fmt.Errorf("part %d is out of range: the document has parts 1 to %d", part, n)
|
|
}
|
|
lo := (part - 1) * each
|
|
hi := min(lo+each, len(data))
|
|
return data[lo:hi], n, nil
|
|
}
|
|
|
|
// joinParts is the parts in order as one document, once its SHA-256 is the one named.
|
|
func joinParts(parts [][]byte, wantSHA string) ([]byte, error) {
|
|
var n int
|
|
for _, p := range parts {
|
|
n += len(p)
|
|
}
|
|
out := make([]byte, 0, n)
|
|
for _, p := range parts {
|
|
out = append(out, p...)
|
|
}
|
|
if got := sha256Hex(out); got != wantSHA {
|
|
return nil, fmt.Errorf("the joined parts' SHA-256 is %s, not the %s named: refused", got, wantSHA)
|
|
}
|
|
return out, nil
|
|
}
|