mirror of
https://github.com/stablyai/orca.git
synced 2026-11-01 09:37:33 +00:00
* feat(mobile): decode page-bundle download windows natively Each 384 KiB window of the page-bundle download cost about 200-300 ms of Hermes JS time (tweetnacl secretbox open, fflate gunzip, a base64 re-encode check and the TextDecoder polyfill), which kept the JS thread busy for the whole download. The phone now opens v2 frames with react-native-libsodium (secretbox open and strict base64) and decodes ranges with a local Expo module, orca-bundle-window-codec (bounded gunzip via system zlib on iOS and java.util.zip.Inflater on Android, plus strict UTF-8). The shared v2 session takes these as optional primitives; the desktop keeps tweetnacl. If any native piece is missing or fails a known-answer probe, the phone keeps today's JS path, with the base64 re-encode replaced by an O(1) canonical check. * refactor(mobile): name the native decode for every inbound frame, not just bundle windows The native secretbox, base64 and UTF-8 primitives decode every inbound v2 frame on the phone, so the files and the Expo module are renamed for what they cover (paired-e2ee-native-*, orca-gzip-utf8-codec). A throw from the native UTF-8 decode now falls back to TextDecoder, since the frame is already open and its counter advanced. * test(mobile): pin the TextDecoder fallback when native UTF-8 throws * fix(mobile): hand the native decode to the transport from the app entry The transport modules are reached by shared, desktop and e2e code. Importing the native bindings there pulled expo-modules-core into the e2e typecheck, whose global types make NODE_ENV required and broke ~20 e2e files. The app entry now provides the bindings; without them the transport runs the JS path. * fix(mobile): a native-codec lookup before the app provides bindings does not pin the JS path
55 lines
1.9 KiB
Swift
55 lines
1.9 KiB
Swift
import Foundation
|
|
|
|
// The codec's decisions, compiled and run without a device, from the module directory:
|
|
//
|
|
// swiftc -O -o /tmp/gzip-utf8-codec-checks ios/BoundedGunzip.swift \
|
|
// ios/StrictUtf8.swift tests/GzipUtf8CodecChecks.swift && /tmp/gzip-utf8-codec-checks
|
|
@main struct GzipUtf8CodecChecks {
|
|
static var failures = 0
|
|
|
|
static func check(_ condition: Bool, _ name: String) {
|
|
if !condition {
|
|
failures += 1
|
|
print("FAIL: \(name)")
|
|
}
|
|
}
|
|
|
|
static func bytes(hex: String) -> Data {
|
|
var data = Data(capacity: hex.count / 2)
|
|
var index = hex.startIndex
|
|
while index < hex.endIndex {
|
|
let next = hex.index(index, offsetBy: 2)
|
|
data.append(UInt8(hex[index..<next], radix: 16)!)
|
|
index = next
|
|
}
|
|
return data
|
|
}
|
|
|
|
static func main() throws {
|
|
// Shared with the Kotlin tests and the JS tests, which pin fflate's answer to the same inputs.
|
|
let url = URL(fileURLWithPath: "tests/gunzip-vectors.json")
|
|
let vectors = try JSONSerialization.jsonObject(with: Data(contentsOf: url)) as! [[String: Any]]
|
|
for vector in vectors {
|
|
let name = vector["name"] as! String
|
|
let expected = (vector["native"] as? String).map { bytes(hex: $0) }
|
|
let actual = try? gunzipBounded(
|
|
bytes(hex: vector["gzipHex"] as! String), outputLimit: vector["outputLimit"] as! Int)
|
|
check(actual == expected, "gunzip: \(name)")
|
|
}
|
|
|
|
let text = "{\"a\":\"x\u{e9}\u{2713}\u{1F642}\"}"
|
|
check(decodeStrictUtf8(Data(text.utf8)) == text, "utf8: every sequence length")
|
|
check(decodeStrictUtf8(Data()) == "", "utf8: empty")
|
|
let refused: [[UInt8]] = [
|
|
[0xef, 0xbb, 0xbf, 0x41], [0xc0, 0x80], [0xed, 0xa0, 0x80], [0x41, 0xe2, 0x82], [0x80],
|
|
[0xf5, 0x80, 0x80, 0x80],
|
|
]
|
|
for input in refused {
|
|
check(decodeStrictUtf8(Data(input)) == nil, "utf8: refuses \(input)")
|
|
}
|
|
|
|
print(failures == 0 ? "all checks passed" : "\(failures) check(s) failed")
|
|
exit(failures == 0 ? 0 : 1)
|
|
}
|
|
}
|