Why does this Dafny function return an empty sequence? - dafny

I'm trying to prove that encoding/decoding a LEB128 (well actually LEB64) varint is lossless. Here's my code:
function decode_varint(input: seq<bv8>) : bv64
requires |input| > 0
{
var byte := input[0];
var val := (byte & 0x7F) as bv64;
var more := byte & 0x80 == 0 && |input| > 1;
if more then val | (decode_varint(input[1..]) << 7) else val
}
function encode_varint(input: bv64) : seq<bv8>
{
var byte := (input & 0x7F) as bv8;
var shifted := input >> 7;
if shifted == 0 then [byte | 0x80] else [byte] + encode_varint(shifted)
}
lemma Lossless(input: bv64) {
var test := encode_varint(128);
var encoded := encode_varint(input);
var decoded := decode_varint(encoded);
assert decoded == input;
}
Unfortunately the assertion doesn't hold. I used the VSCode plugin's counter-example feature (F7) to inspect the values in Lossless, and it picks input = 0x8000000000000000. Fine, I think that should work, but the counter-example also shows that test is test:seq<bv8> = ().
I don't understand that. Firstly don't sequences use square brackets? Second it doesn't look like it is possible for encode_varint() to return an empty sequence in any case. In fact Dafny proves this successfully!
lemma NeverEmpty(input: bv64) {
var encoded := encode_varint(input);
assert |encoded| > 0;
}
What's going on here?
Edit: Also if I add these examples...
lemma Examples()
{
assert encode_varint(1 << 7) == [0x00, 0x81];
assert encode_varint(1 << 14) == [0x00, 0x00, 0x81];
assert encode_varint(1 << 28) == [0x00, 0x00, 0x00, 0x00, 0x81];
assert encode_varint(1 << 42) == [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81];
assert encode_varint(1 << 56) == [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81];
assert encode_varint(1 << 63) == [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81];
assert encode_varint(0x8000000000000000) == [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81];
}
Then they are all proven, but if I only have the last example (the counter-example Dafny generates) then it isn't!

The fact that decode(encode(x)) == x requires inductive proof (since the functions are recursive). Luckily, Dafny's automatic induction is smart enough to prove it if you state it as a lemma with a postcondition like this:
lemma Lossless(input: bv64)
ensures decode_varint(encode_varint(input)) == input
{}
You can then call that lemma to prove the assert.
lemma Main(input: bv64) {
var test := encode_varint(128);
var encoded := encode_varint(input);
var decoded := decode_varint(encoded);
Lossless(input);
assert decoded == input;
}
(If you're wondering why Dafny proves my lemma automatically but not the assert, one reason is because (roughly speaking) Dafny only attempts automatic induction on lemmas, not on asserts.)
And let me never pass up an occasion to mention the slogan "Just because Dafny reports an error doesn't mean the program is incorrect"

Related

Gnu radio and Yaesu CAT over serial

I'm trying to take info from my radio (ft817) over serial connection and give them to gnuradio in order to use them to make a nice GUI for a panadapter. I normally print CAT data like this, please excuse my python skills I'm still learning :
`
import serial
import time
SERIAL_PORT = "/dev/ttyUSB0"
SAMPLES_PER_SEC = 5
class FT817(object):
SERIAL_SPEED = 9600
SERIAL_STOPBITS = serial.STOPBITS_TWO
SERIAL_TIMEOUT = 1.0
# Transceiver modes and commands
MODES = ["LSB", "USB", "CW", "CWR", "AM", None, "WFM", None, "FM", None, "DIG", None, "PKT"]
CMD_READ_FREQ = [0x00, 0x00, 0x00, 0x00, 0x03]
CMD_READ_RX_STATUS = [0x00, 0x00, 0x00, 0x00, 0xE7]
def __init__(self, serial_port, serial_speed=SERIAL_SPEED, serial_stopbits=SERIAL_STOPBITS):
self._serial = serial.Serial(serial_port, serial_speed, stopbits=serial_stopbits, timeout=FT817.SERIAL_TIMEOUT)
self._frequency = ""
self._mode = ""
self._squelch = True
self._s_meter = ""
def read_frequency(self):
'''Queries transceiver RX frequency and mode.
The response is 5 bytes: first four store frequency and
the fifth stores mode (AM, FM, SSB etc.)
'''
cmd = FT817.CMD_READ_FREQ
self._serial.write(cmd)
resp = self._serial.read(5)
resp_bytes = ((resp[0]), (resp[1]), (resp[2]), (resp[3]))
self._frequency = "%02x%02x%02x%02x" % resp_bytes
self._mode = FT817.MODES[(resp[4])]
def read_rx_status(self):
'''Queries transceiver RX status.
The response is 1 byte:
bit 7: Squelch status: 0 - off (signal present), 1 - on (no signal)
bit 6: CTCSS/DCS Code: 0 - code is matched, 1 - code is unmatched
bit 5: Discriminator centering: 0 - discriminator centered, 1 - uncentered
bit 4: Dummy data
bit 3-0: S Meter data
'''
cmd = FT817.CMD_READ_RX_STATUS
self._serial.write(cmd)
resp = self._serial.read(1)
resp_byte = (resp[0])
self._squelch = True if (resp_byte & 0B10000000) else False
def get_trx_state_string(self):
'''Returns transceiver state data for printing.
'''
sql_str = 'SQL: ON' if self._squelch else 'SQL: OFF'
res = "%sHz %s %s" % (self._frequency, self._mode, sql_str)
return res
def __str__(self):
'''Overrides __str__() method for using FT817 class with print command.
'''
return self.get_trx_state_string()
if __name__ == '__main__':
print ("Starting FT-817ND monitor...")
try:
ft817 = FT817(SERIAL_PORT)
delay = 1.0 / SAMPLES_PER_SEC
while True:
ft817.read_frequency()
ft817.read_rx_status()
print
print (ft817)
time.sleep(delay)
except KeyboardInterrupt:
# KeyboardInterrupt exception is thrown when CTRL-C or CTRL-Break is pressed.
pass
finally:
print ("See you later. 73!")
`
But i'm not sure how to implement this in GNURADIO, any help?
I've tried to use py block but without success.

PEC calculation in Lua

I am struggling to calculate the packet error code (PEC) of received data over I2C in order to know check if the data is valid.
PEC Definition
I used the code stated in a previous question but it does not work for me.
The data looks like this: 0x00, 0x07, 0x01, 0x12, 0x3b, 0xd5
PEC is 0xd5 which is based on the polynomial = x^8+ x^2+ x^1+ x^0 - 0x107
This works also fine with this calculator.
So my question is, where is the difference between the code from the website and the one from the linked question:
local function crc8(t)
local c = 0
for _, b in ipairs(t) do
for i = 0, 7 do
c = c >> 1 ~ ((c ~ b >> i) & 1) * 0xE0
end
end
return c
end
This definition of CRC uses reversed bits in all data bytes.
local function reverse(x)
-- reverse bits of a byte
local y = 0
for j = 1, 8 do
y = y * 2 + (x&1)
x = x >> 1
end
return y
end
local function crc8(t)
local c = 0
for _, b in ipairs(t) do
b = reverse(b)
for i = 0, 7 do
c = c >> 1 ~ ((c ~ b >> i) & 1) * 0xE0
end
end
c = reverse(c)
return c
end
print(tohex(crc8{0x00, 0x07, 0x01, 0x12, 0x3b})) --> 0xd5

swift extracting bytes from struct Data

data is arriving in the form of struct Data size count == 5 the last 3 bytes contain an ID that needs to be extracted.
The following code works, however I am sure it could be greatly improved (as you can tell I am new to swift!):
var data:[UInt8] = [ 0xff, 0xff, 0x01, 0x02 ,0x03 ]
var txData = Data(bytes: data)
print(txData.count)
let byte00 = txData.subdata(in: 2..<3 ).withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> UInt8 in
return ptr.pointee
}
let byte01 = txData.subdata(in: 3..<4 ).withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> UInt8 in
return ptr.pointee
}
let byte02 = txData.subdata(in: 4..<5 ).withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> UInt8 in
return ptr.pointee
}
let rxData = (UInt(byte00) << 16) + (UInt(byte01) << 8) + UInt(byte02)
print( String(rxData, radix:16) )
Any tutorial recommendations covering this area of swift would be greatly appreciated.
You can write something like this:
var data:[UInt8] = [ 0xff, 0xff, 0x01, 0x02 ,0x03 ]
var txData = Data(bytes: data)
print(txData.count)
let byte00 = txData[2]
let byte01 = txData[3]
let byte02 = txData[4]
let rxData = (UInt(byte00) << 16) + (UInt(byte01) << 8) + UInt(byte02)
print( String(rxData, radix:16) ) //->10203
In Swift 3, Data can be treated as a Collection of UInt8, you can subscript to Data directly when getting each byte as UInt8.
And as 3 byte is not a good number for the current CPUs, the code above cannot be much shorter.

How do you convert 8-bit bytes to 6-bit characters?

I have a specific requirement to convert a stream of bytes into a character encoding that happens to be 6-bits per character.
Here's an example:
Input: 0x50 0x11 0xa0
Character Table:
010100 T
000001 A
000110 F
100000 SPACE
Output: "TAF "
Logically I can understand how this works:
Taking 0x50 0x11 0xa0 and showing as binary:
01010000 00010001 10100000
Which is "TAF ".
What's the best way to do this programmatically (pseudo code or c++). Thank you!
Well, every 3 bytes, you end up with four characters. So for one thing, you need to work out what to do if the input isn't a multiple of three bytes. (Does it have padding of some kind, like base64?)
Then I'd probably take each 3 bytes in turn. In C#, which is close enough to pseudo-code for C :)
for (int i = 0; i < array.Length; i += 3)
{
// Top 6 bits of byte i
int value1 = array[i] >> 2;
// Bottom 2 bits of byte i, top 4 bits of byte i+1
int value2 = ((array[i] & 0x3) << 4) | (array[i + 1] >> 4);
// Bottom 4 bits of byte i+1, top 2 bits of byte i+2
int value3 = ((array[i + 1] & 0xf) << 2) | (array[i + 2] >> 6);
// Bottom 6 bits of byte i+2
int value4 = array[i + 2] & 0x3f;
// Now use value1...value4, e.g. putting them into a char array.
// You'll need to decode from the 6-bit number (0-63) to the character.
}
Just in case if someone is interested - another variant that extracts 6-bit numbers from the stream as soon as they appear there. That is, results can be obtained even if less then 3 bytes are currently read. Would be useful for unpadded streams.
The code saves the state of the accumulator a in variable n which stores the number of bits left in accumulator from the previous read.
int n = 0;
unsigned char a = 0;
unsigned char b = 0;
while (read_byte(&byte)) {
// save (6-n) most significant bits of input byte to proper position
// in accumulator
a |= (b >> (n + 2)) & (077 >> n);
store_6bit(a);
a = 0;
// save remaining least significant bits of input byte to proper
// position in accumulator
a |= (b << (4 - n)) & ((077 << (4 - n)) & 077);
if (n == 4) {
store_6bit(a);
a = 0;
}
n = (n + 2) % 6;
}

PNG validation on iOS

Writing a mapping application on iOS, making use of OpenStreetMap tiles.
Map tile images are downloaded asynchronously and stored in a dictionary, or persisted in a SQLite DB.
Occasionally, for whatever reason, while attempting to render a map tile image, I get the following error:
ImageIO: <ERROR> PNGinvalid distance too far back
This causes nasty black squares to appear over my map.
This is the piece of code in which this occurs:
NSData *imageData = [TileDownloader RetrieveDataAtTileX:(int)tilex Y:(int)tiley Zoom:(int)zoomLevel];
if (imageData != nil) {
NSLog(#"Obtained image data\n");
UIImage *img = [[UIImage imageWithData:imageData] retain];
// Perform the image render on the current UI context.
// ERROR OCCURS BETWEEN PUSH AND POP
UIGraphicsPushContext(context);
[img drawInRect:[self rectForMapRect:mapRect] blendMode:kCGBlendModeNormal alpha:1.0f];
UIGraphicsPopContext();
[img release];
}
Now, what I'm looking for is a way to ensure a png is valid before attempting to render it to my map.
Edit: The system also occasionally throws this error:
ImageIO: <ERROR> PNGIDAT: CRC error
I found this in other question and put together what solved the issue for me. Hope you find this helpful.
The PNG format has several built in checks. Each "chunk" has a CRC32 check, but to check that you'd need to read the full file.
A more basic check (not foolproof, of course) would be to read the start and ending of the file.
The first 8 bytes should always be the following (decimal) values { 137, 80, 78, 71, 13, 10, 26, 10 } (ref). In particular, the bytes second-to-fourth correspond to the ASCII string "PNG".
In hexadecimal:
89 50 4e 47 0d 0a 1a 0a
.. P N G ...........
You can also check the last 12 bytes of the file (IEND chunk). The middle 4 bytes should correspond to the ASCII string "IEND". More specifically the last 12 bytes should be (in hexa):
00 00 00 00 49 45 4e 44 ae 42 60 82
........... I E N D ...........
(Strictly speaking, it's not really obligatory for a PNG file to end with those 12 bytes, the IEND chunk itself signals the end of the PNG stream and so a file could in principle have extra trailing bytes which would be ignored by the PNG reader. In practice, this is extremely improbable).
Here is an implementation:
- (BOOL)dataIsValidPNG:(NSData *)data
{
if (!data || data.length < 12)
{
return NO;
}
NSInteger totalBytes = data.length;
const char *bytes = (const char *)[data bytes];
return (bytes[0] == (char)0x89 && // PNG
bytes[1] == (char)0x50 &&
bytes[2] == (char)0x4e &&
bytes[3] == (char)0x47 &&
bytes[4] == (char)0x0d &&
bytes[5] == (char)0x0a &&
bytes[6] == (char)0x1a &&
bytes[7] == (char)0x0a &&
bytes[totalBytes - 12] == (char)0x00 && // IEND
bytes[totalBytes - 11] == (char)0x00 &&
bytes[totalBytes - 10] == (char)0x00 &&
bytes[totalBytes - 9] == (char)0x00 &&
bytes[totalBytes - 8] == (char)0x49 &&
bytes[totalBytes - 7] == (char)0x45 &&
bytes[totalBytes - 6] == (char)0x4e &&
bytes[totalBytes - 5] == (char)0x44 &&
bytes[totalBytes - 4] == (char)0xae &&
bytes[totalBytes - 3] == (char)0x42 &&
bytes[totalBytes - 2] == (char)0x60 &&
bytes[totalBytes - 1] == (char)0x82);
}
I know this is a super old thread, but I was looking around for an NSData extension that actually validated the crc32's in the PNG data chunks. Having not found one, I adapted one from some other source.
This will actually flag bad PNG CRC's, which isn't done (shockingly) by most image libraries
static const unsigned int datacrc32_table[256] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
0x2d02ef8d
};
unsigned int
datacrc32 (unsigned int crc, unsigned char *buf, int len)
{
unsigned char *end;
crc = ~crc;
for (end = buf + len; buf < end; ++buf)
crc = datacrc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
return ~crc;
}
-(BOOL)isCRCValidPNG {
char chnk [5];
int l = 0;
int size = (int)[self length];
unsigned int crc = 0;
unsigned char c;
unsigned int csum = 0;
unsigned char b;
unsigned char *tileBytes = (unsigned char *)[self bytes];
if (self.length > 8){
const unsigned char pngHeaderBytes[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
for (int i = 0 ; i < 8 ; ++i){
if (tileBytes[i] != pngHeaderBytes[i])
return NO;
}
}
// process chunks
int bytePtr = 8;
strcpy (chnk, "");
do
{
// get chunk size
if (bytePtr+4 > size)
return NO;
l = 0;
for (int i = 0; i < 4; i++)
{
l = (l << 8) + tileBytes[bytePtr++];
}
printf("l is %08x",l);
// get chunk name
crc = 0;
strcpy (chnk, "");
if (bytePtr+4 > size)
return NO;
for (int i = 0; i < 4; i++)
{
c = tileBytes[bytePtr++];
crc = datacrc32 (crc, &c, 1);
chnk[i] = (char) c;
}
chnk[4] = '\0';
printf ("%s (%3d )", chnk, l);
// chunk data
if (bytePtr+l > size)
return NO;
for (int i = 0; i < l; i++)
{
c = tileBytes[bytePtr++];
crc = datacrc32 (crc, &c, 1);
}
// checksum
csum = 0;
if (bytePtr+4 > size)
return NO;
for (int i = 0; i < 4; i++)
{
c = tileBytes[bytePtr++];
csum = (csum << 8) + (int) c;
b = (unsigned char) ((crc >> 8 * (3 - i)) & 0xFF);
// printf ("b = %02x\n", b);
}
if (crc == csum)
NSLog(#"Chunk %s validated",chnk);
else
NSLog(#"chunk %s invalid ",chnk);
if (crc != csum)
return NO;
}
while (strcmp (chnk, "IEND") != 0);
return YES;
}
Switched from my own Asynchronous Download Queue Manager to the All Seeing I implementation. Problem became a moot point.
The Swift Version
func checkPNGImageDataFormat(_ imageData:Data) -> Bool
{
//More expensive since it has to go through entire data
//Check entire header magic number and IEND trailer in PNG data
var status:Bool = true
if(imageData.count < 12)
{
return false
}
let totalBytes = imageData.count
let bytes = imageData.withUnsafeBytes {
[UInt8](UnsafeBufferPointer(start: $0, count: totalBytes))
}
let header:Bool = bytes[0] == 0x89 && bytes[1] == 0x50 && bytes[2] == 0x4e && bytes[3] == 0x47 && bytes[4] == 0x0d && bytes[5] == 0x0a && bytes[6] == 0x1a && bytes[7] == 0x0a
let iend:Bool = bytes[totalBytes - 12] == 0x00 && bytes[totalBytes - 11] == 0x00 && bytes[totalBytes - 10] == 0x00 && bytes[totalBytes - 9] == 0x00 && bytes[totalBytes - 8] == 0x49 && bytes[totalBytes - 7] == 0x45 && bytes[totalBytes - 6] == 0x4e && bytes[totalBytes - 5] == 0x44 && bytes[totalBytes - 4] == 0xae && bytes[totalBytes - 3] == 0x42 && bytes[totalBytes - 2] == 0x60 && bytes[totalBytes - 1] == 0x82
status = header && iend
return status
}

Resources