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 * as protobuf from 'protobufjs/minimal'; 16import {assertTrue} from '../base/logging'; 17 18import {ProtoRingBuffer} from './proto_ring_buffer'; 19 20let seed = 1; 21 22// For reproducibility. 23function Rnd(max: number) { 24 seed = seed * 16807 % 2147483647; 25 return seed % max; 26} 27 28function MakeProtoMessage(fieldId: number, len: number) { 29 const writer = protobuf.Writer.create(); 30 const tag = (fieldId << 3) | 2 /*length-delimited*/; 31 assertTrue(tag < 0x80 && (tag & 7) === 2); 32 writer.uint32(tag); 33 const data = new Uint8Array(len); 34 for (let i = 0; i < len; i++) { 35 data[i] = 48 + ((fieldId + i) % 73); 36 } 37 writer.bytes(data); 38 const res = writer.finish(); 39 // For whatever reason the object returned by protobufjs' Writer cannot be 40 // directly .toEqual()-ed with Uint8Arrays. 41 const buf = new Uint8Array(res.length); 42 buf.set(res); 43 return buf; 44} 45 46test('ProtoRingBufferTest.Fastpath', () => { 47 const buf = new ProtoRingBuffer(); 48 49 for (let rep = 0; rep < 3; rep++) { 50 let inputBuf = MakeProtoMessage(1, 32); 51 buf.append(inputBuf); 52 let msg = buf.readMessage(); 53 expect(msg).toBeDefined(); 54 expect(msg).toBeInstanceOf(Uint8Array); 55 expect(msg!.length).toBe(32); 56 57 // subarray(2) is to strip the proto preamble. The returned buffer starts at 58 // the start of the payload. 59 expect(msg).toEqual(inputBuf.subarray(2)); 60 61 // When we hit the fastpath, the returned message should be a subarray of 62 // the same ArrayBuffer passed to append. 63 expect(msg!.buffer).toBe(inputBuf.buffer); 64 65 inputBuf = MakeProtoMessage(2, 32); 66 buf.append(inputBuf.subarray(0, 13)); 67 expect(buf.readMessage()).toBeUndefined(); 68 buf.append(inputBuf.subarray(13)); 69 msg = buf.readMessage(); 70 expect(msg).toBeDefined(); 71 expect(msg).toBeInstanceOf(Uint8Array); 72 expect(msg).toEqual(inputBuf.subarray(2)); 73 expect(msg!.buffer !== inputBuf.buffer).toBeTruthy(); 74 } 75}); 76 77test('ProtoRingBufferTest.CoalescingStream', () => { 78 const buf = new ProtoRingBuffer(); 79 80 const mergedBuf = new Uint8Array(612); 81 const expected = new Array<Uint8Array>(); 82 for (let i = 1, pos = 0; i <= 6; i++) { 83 const msg = MakeProtoMessage(i, 100); 84 expected.push(msg); 85 mergedBuf.set(msg, pos); 86 pos += msg.length; 87 } 88 89 const fragLens = [120, 20, 471, 1]; 90 let fragSum = 0; 91 fragLens.map(fragLen => { 92 buf.append(mergedBuf.subarray(fragSum, fragSum + fragLen)); 93 fragSum += fragLen; 94 for (;;) { 95 const msg = buf.readMessage(); 96 if (msg === undefined) break; 97 const exp = expected.shift(); 98 expect(exp).toBeDefined(); 99 expect(msg).toEqual(exp!.subarray(-1 * msg.length)); 100 } 101 }); 102 expect(expected.length).toEqual(0); 103}); 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