///|
/// Split raw bytes into independently decodable symbols with a shared file ID.
pub fn encode_append_bytes(
data : Bytes,
file_id1 : Int,
file_id2 : Int,
options? : EncodeOptions = { shape: Square, },
eci? : Int? = None,
) -> Array[Symbol] raise EncodeError {
if !valid_append({ index: 1, count: 2, file_id1, file_id2, }) {
raise InvalidOption("invalid append file ID")
}
let single = try {
let segments = match eci {
Some(e) => [Eci(e), Data(data)]
None => [Data(data)]
}
Some(encode_message(segments, options=MessageOptions::new(symbol=options)))
} catch {
CapacityExceeded => None
e => raise e
}
if single is Some(symbol) {
return [symbol]
}
let parts : Array[Bytes] = []
let mut offset = 0
while offset < data.length() || parts.is_empty() {
if parts.length() >= 16 {
raise CapacityExceeded
}
let mut lo = 0
let mut hi = data.length() - offset
if hi > 3116 {
hi = 3116
}
let mut best = -1
while lo <= hi {
let n = (lo + hi) / 2
let bytes = Bytes::makei(n, fn(i) { data[offset + i] })
let segments = match eci {
Some(e) => [Eci(e), Data(bytes)]
None => [Data(bytes)]
}
let fits = try {
ignore(
encode_message(
segments,
options=MessageOptions::new(
symbol=options,
append=Some({ index: 1, count: 16, file_id1, file_id2, }),
),
),
)
true
} catch {
CapacityExceeded => false
e => raise e
}
if fits {
best = n
lo = n + 1
} else {
hi = n - 1
}
}
if best < 0 || (best == 0 && offset < data.length()) {
raise CapacityExceeded
}
parts.push(Bytes::makei(best, fn(i) { data[offset + i] }))
offset += best
}
if parts.length() == 1 {
let segments = match eci {
Some(e) => [Eci(e), Data(parts[0])]
None => [Data(parts[0])]
}
return [
encode_message(segments, options=MessageOptions::new(symbol=options)),
]
}
let result = []
for i in 0.. [Eci(e), Data(parts[i])]
None => [Data(parts[i])]
}
result.push(
encode_message(
segments,
options=MessageOptions::new(
symbol=options,
append=Some({
index: i + 1,
count: parts.length(),
file_id1,
file_id2,
}),
),
),
)
}
result
}
///|
/// Split UTF-8 text only between complete Unicode characters.
pub fn encode_append_text(
text : String,
file_id1 : Int,
file_id2 : Int,
options? : EncodeOptions = { shape: Square, },
) -> Array[Symbol] raise EncodeError {
if !valid_append({ index: 1, count: 2, file_id1, file_id2, }) {
raise InvalidOption("invalid append file ID")
}
let single = Some(encode_text(text, options~)) catch {
CapacityExceeded => None
e => raise e
}
if single is Some(symbol) {
return [symbol]
}
let chars = text.to_array()
let parts : Array[String] = []
let mut offset = 0
while offset < chars.length() || parts.is_empty() {
if parts.length() >= 16 {
raise CapacityExceeded
}
let mut lo = 0
let mut hi = chars.length() - offset
if hi > 3116 {
hi = 3116
}
let mut best = -1
let mut chosen = ""
while lo <= hi {
let n = (lo + hi) / 2
let builder = StringBuilder()
for i in 0.. false
e => raise e
}
if fits {
best = n
chosen = part
lo = n + 1
} else {
hi = n - 1
}
}
if best < 0 || (best == 0 && offset < chars.length()) {
raise CapacityExceeded
}
parts.push(chosen)
offset += best
}
if parts.length() == 1 {
return [encode_text(parts[0], options~)]
}
let result = []
for i in 0.. DecodedMessage raise DecodeError {
if parts.is_empty() {
raise InvalidCodeword("empty append set")
}
let first = match parts[0].append {
Some(a) => a
None => {
if parts.length() == 1 {
return parts[0]
}
raise InvalidCodeword("missing append header")
}
}
if parts.length() != first.count {
raise InvalidCodeword("missing or excess append parts")
}
let slots : Array[DecodedMessage?] = Array::make(first.count, None)
for p in parts {
let a = match p.append {
Some(a) => a
None => raise InvalidCodeword("missing append header")
}
if a.count != first.count ||
a.file_id1 != first.file_id1 ||
a.file_id2 != first.file_id2 ||
p.gs1 != parts[0].gs1 ||
p.macro_kind != 0 ||
p.reader_programming {
raise InvalidCodeword("inconsistent append metadata")
}
if slots[a.index - 1] is Some(_) {
raise InvalidCodeword("duplicate append index")
}
slots[a.index - 1] = Some(p)
}
let payload : Array[Byte] = []
let ecis = []
let separators = []
let mut corrected = 0
for slot in slots {
let p = match slot {
Some(p) => p
None => raise InvalidCodeword("missing append part")
}
let offset = payload.length()
ecis.push((offset, 3))
for (pos, eci) in p.ecis {
ecis.push((offset + pos, eci))
}
for pos in p.separators {
separators.push(offset + pos)
}
for byte in p.payload {
payload.push(byte)
}
corrected += p.corrected_codewords
}
{
payload: Bytes::from_array(payload),
ecis,
separators,
gs1: parts[0].gs1,
macro_kind: 0,
reader_programming: false,
append: None,
corrected_codewords: corrected,
}
}