Files
mesh-catalog/modules/brother/cmd/brother/parts.go
T

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
}