• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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