| // Copyright (C) 2021 The Android Open Source Project | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | import protobuf from 'protobufjs/minimal'; | 
 | import {assertTrue} from '../base/logging'; | 
 |  | 
 | import {ProtoRingBuffer} from './proto_ring_buffer'; | 
 |  | 
 | let seed = 1; | 
 |  | 
 | // For reproducibility. | 
 | function Rnd(max: number) { | 
 |   seed = seed * 16807 % 2147483647; | 
 |   return seed % max; | 
 | } | 
 |  | 
 | function MakeProtoMessage(fieldId: number, len: number) { | 
 |   const writer = protobuf.Writer.create(); | 
 |   const tag = (fieldId << 3) | 2; | 
 |   assertTrue(tag < 0x80 && (tag & 7) === 2); | 
 |   writer.uint32(tag); | 
 |   const data = new Uint8Array(len); | 
 |   for (let i = 0; i < len; i++) { | 
 |     data[i] = 48 + ((fieldId + i) % 73); | 
 |   } | 
 |   writer.bytes(data); | 
 |   const res = writer.finish(); | 
 |   // For whatever reason the object returned by protobufjs' Writer cannot be | 
 |   // directly .toEqual()-ed with Uint8Arrays. | 
 |   const buf = new Uint8Array(res.length); | 
 |   buf.set(res); | 
 |   return buf; | 
 | } | 
 |  | 
 | test('ProtoRingBufferTest.Fastpath', () => { | 
 |   const buf = new ProtoRingBuffer(); | 
 |  | 
 |   for (let rep = 0; rep < 3; rep++) { | 
 |     let inputBuf = MakeProtoMessage(1, 32); | 
 |     buf.append(inputBuf); | 
 |     let msg = buf.readMessage(); | 
 |     expect(msg).toBeDefined(); | 
 |     expect(msg).toBeInstanceOf(Uint8Array); | 
 |     expect(msg!.length).toBe(32); | 
 |  | 
 |     // subarray(2) is to strip the proto preamble. The returned buffer starts at | 
 |     // the start of the payload. | 
 |     expect(msg).toEqual(inputBuf.subarray(2)); | 
 |  | 
 |     // When we hit the fastpath, the returned message should be a subarray of | 
 |     // the same ArrayBuffer passed to append. | 
 |     expect(msg!.buffer).toBe(inputBuf.buffer); | 
 |  | 
 |     inputBuf = MakeProtoMessage(2, 32); | 
 |     buf.append(inputBuf.subarray(0, 13)); | 
 |     expect(buf.readMessage()).toBeUndefined(); | 
 |     buf.append(inputBuf.subarray(13)); | 
 |     msg = buf.readMessage(); | 
 |     expect(msg).toBeDefined(); | 
 |     expect(msg).toBeInstanceOf(Uint8Array); | 
 |     expect(msg).toEqual(inputBuf.subarray(2)); | 
 |     expect(msg!.buffer !== inputBuf.buffer).toBeTruthy(); | 
 |   } | 
 | }); | 
 |  | 
 | test('ProtoRingBufferTest.CoalescingStream', () => { | 
 |   const buf = new ProtoRingBuffer(); | 
 |  | 
 |   const mergedBuf = new Uint8Array(612); | 
 |   const expected = new Array<Uint8Array>(); | 
 |   for (let i = 1, pos = 0; i <= 6; i++) { | 
 |     const msg = MakeProtoMessage(i, 100); | 
 |     expected.push(msg); | 
 |     mergedBuf.set(msg, pos); | 
 |     pos += msg.length; | 
 |   } | 
 |  | 
 |   const fragLens = [120, 20, 471, 1]; | 
 |   let fragSum = 0; | 
 |   fragLens.map((fragLen) => { | 
 |     buf.append(mergedBuf.subarray(fragSum, fragSum + fragLen)); | 
 |     fragSum += fragLen; | 
 |     for (;;) { | 
 |       const msg = buf.readMessage(); | 
 |       if (msg === undefined) break; | 
 |       const exp = expected.shift(); | 
 |       expect(exp).toBeDefined(); | 
 |       expect(msg).toEqual(exp!.subarray(-1 * msg.length)); | 
 |     } | 
 |   }); | 
 |   expect(expected.length).toEqual(0); | 
 | }); | 
 |  | 
 |  | 
 | test('ProtoRingBufferTest.RandomSizes', () => { | 
 |   const buf = new ProtoRingBuffer(); | 
 |   const kNumMsg = 100; | 
 |   const mergedBuf = new Uint8Array(1024 * 1024 * 32); | 
 |   const expectedLengths = []; | 
 |   let mergedLen = 0; | 
 |   for (let i = 0; i < kNumMsg; i++) { | 
 |     const fieldId = 1 + Rnd(15);  // We support only one byte tag. | 
 |     const rndVal = Rnd(1024); | 
 |     let len = 1 + rndVal; | 
 |     if ((rndVal % 100) < 5) { | 
 |       len *= 1000; | 
 |     } | 
 |     const msg = MakeProtoMessage(fieldId, len); | 
 |     assertTrue(mergedBuf.length >= mergedLen + msg.length); | 
 |     expectedLengths.push(len); | 
 |     mergedBuf.set(msg, mergedLen); | 
 |     mergedLen += msg.length; | 
 |   } | 
 |  | 
 |   for (let fragSum = 0; fragSum < mergedLen; /**/) { | 
 |     let fragLen = 1 + Rnd(1024 * 32); | 
 |     fragLen = Math.min(fragLen, mergedLen - fragSum); | 
 |     buf.append(mergedBuf.subarray(fragSum, fragSum + fragLen)); | 
 |     fragSum += fragLen; | 
 |     for (;;) { | 
 |       const msg = buf.readMessage(); | 
 |       if (msg === undefined) break; | 
 |       const expLen = expectedLengths.shift(); | 
 |       expect(expLen).toBeDefined(); | 
 |       expect(msg.length).toEqual(expLen); | 
 |     } | 
 |   } | 
 |   expect(expectedLengths.length).toEqual(0); | 
 | }); |