1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import protobuf from 'protobufjs/minimal'; 16 17import {assertTrue} from '../base/logging'; 18 19import {ProtoRingBuffer} from './proto_ring_buffer'; 20 21let seed = 1; 22 23// For reproducibility. 24function Rnd(max: number) { 25 seed = (seed * 16807) % 2147483647; 26 return seed % max; 27} 28 29function MakeProtoMessage(fieldId: number, len: number) { 30 const writer = protobuf.Writer.create(); 31 const tag = (fieldId << 3) | 2; 32 assertTrue(tag < 0x80 && (tag & 7) === 2); 33 writer.uint32(tag); 34 const data = new Uint8Array(len); 35 for (let i = 0; i < len; i++) { 36 data[i] = 48 + ((fieldId + i) % 73); 37 } 38 writer.bytes(data); 39 const res = writer.finish(); 40 // For whatever reason the object returned by protobufjs' Writer cannot be 41 // directly .toEqual()-ed with Uint8Arrays. 42 const buf = new Uint8Array(res.length); 43 buf.set(res); 44 return buf; 45} 46 47test('ProtoRingBufferTest.Fastpath', () => { 48 const buf = new ProtoRingBuffer(); 49 50 for (let rep = 0; rep < 3; rep++) { 51 let inputBuf = MakeProtoMessage(1, 32); 52 buf.append(inputBuf); 53 let msg = buf.readMessage(); 54 expect(msg).toBeDefined(); 55 expect(msg).toBeInstanceOf(Uint8Array); 56 expect(msg!.length).toBe(32); 57 58 // subarray(2) is to strip the proto preamble. The returned buffer starts at 59 // the start of the payload. 60 expect(msg).toEqual(inputBuf.subarray(2)); 61 62 // When we hit the fastpath, the returned message should be a subarray of 63 // the same ArrayBuffer passed to append. 64 expect(msg!.buffer).toBe(inputBuf.buffer); 65 66 inputBuf = MakeProtoMessage(2, 32); 67 buf.append(inputBuf.subarray(0, 13)); 68 expect(buf.readMessage()).toBeUndefined(); 69 buf.append(inputBuf.subarray(13)); 70 msg = buf.readMessage(); 71 expect(msg).toBeDefined(); 72 expect(msg).toBeInstanceOf(Uint8Array); 73 expect(msg).toEqual(inputBuf.subarray(2)); 74 expect(msg!.buffer !== inputBuf.buffer).toBeTruthy(); 75 } 76}); 77 78test('ProtoRingBufferTest.CoalescingStream', () => { 79 const buf = new ProtoRingBuffer(); 80 81 const mergedBuf = new Uint8Array(612); 82 const expected = new Array<Uint8Array>(); 83 for (let i = 1, pos = 0; i <= 6; i++) { 84 const msg = MakeProtoMessage(i, 100); 85 expected.push(msg); 86 mergedBuf.set(msg, pos); 87 pos += msg.length; 88 } 89 90 const fragLens = [120, 20, 471, 1]; 91 let fragSum = 0; 92 fragLens.map((fragLen) => { 93 buf.append(mergedBuf.subarray(fragSum, fragSum + fragLen)); 94 fragSum += fragLen; 95 for (;;) { 96 const msg = buf.readMessage(); 97 if (msg === undefined) break; 98 const exp = expected.shift(); 99 expect(exp).toBeDefined(); 100 expect(msg).toEqual(exp!.subarray(-1 * msg.length)); 101 } 102 }); 103 expect(expected.length).toEqual(0); 104}); 105 106test('ProtoRingBufferTest.RandomSizes', () => { 107 const buf = new ProtoRingBuffer(); 108 const kNumMsg = 100; 109 const mergedBuf = new Uint8Array(1024 * 1024 * 32); 110 const expectedLengths = []; 111 let mergedLen = 0; 112 for (let i = 0; i < kNumMsg; i++) { 113 const fieldId = 1 + Rnd(15); // We support only one byte tag. 114 const rndVal = Rnd(1024); 115 let len = 1 + rndVal; 116 if (rndVal % 100 < 5) { 117 len *= 1000; 118 } 119 const msg = MakeProtoMessage(fieldId, len); 120 assertTrue(mergedBuf.length >= mergedLen + msg.length); 121 expectedLengths.push(len); 122 mergedBuf.set(msg, mergedLen); 123 mergedLen += msg.length; 124 } 125 126 for (let fragSum = 0; fragSum < mergedLen /**/; ) { 127 let fragLen = 1 + Rnd(1024 * 32); 128 fragLen = Math.min(fragLen, mergedLen - fragSum); 129 buf.append(mergedBuf.subarray(fragSum, fragSum + fragLen)); 130 fragSum += fragLen; 131 for (;;) { 132 const msg = buf.readMessage(); 133 if (msg === undefined) break; 134 const expLen = expectedLengths.shift(); 135 expect(expLen).toBeDefined(); 136 expect(msg.length).toEqual(expLen); 137 } 138 } 139 expect(expectedLengths.length).toEqual(0); 140}); 141