1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.cts.verifier.audio.wavelib;
18
19// Non-blocking pipe supports a single writer and single reader.
20// The write side of a pipe permits overruns; flow control is the caller's responsibility.
21
22public class PipeShort {
23    private int mFront;
24    private int mRear;
25    private short mBuffer[];
26    private volatile int mVolatileRear; // written by write(), read by read()
27    private int mMaxValues;
28    private int mBytesOverrun;
29    private int mOverruns;
30    public static final int OVERRUN = -2;
31
32    // maxBytes will be rounded up to a power of 2, and all slots are available. Must be >= 2.
33    public PipeShort(int maxValues)
34    {
35        mMaxValues = roundup(maxValues);
36        mBuffer = new short[mMaxValues];
37    }
38
39    // buffer must != null.
40    // offset must be >= 0.
41    // count is maximum number of bytes to copy, and must be >= 0.
42    // offset + count must be <= buffer.length.
43    // Returns actual number of bytes copied >= 0.
44    public int write(short[] buffer, int offset, int count)
45    {
46        int rear = mRear & (mMaxValues - 1);
47        int written = mMaxValues - rear;
48        if (written > count) {
49            written = count;
50        }
51        System.arraycopy(buffer, offset, mBuffer, rear, written);
52        if (rear + written == mMaxValues) {
53            if ((count -= written) > rear) {
54                count = rear;
55            }
56            if (count > 0) {
57                System.arraycopy(buffer, offset + written, mBuffer, 0, count);
58                written += count;
59            }
60        }
61        mRear += written;
62        mVolatileRear = mRear;
63        return written;
64    }
65
66    public int availableToRead()
67    {
68        int rear = mVolatileRear;
69        int avail = rear - mFront;
70        if (avail > mMaxValues) {
71            // Discard 1/16 of the most recent data in pipe to avoid another overrun immediately
72            int oldFront = mFront;
73            mFront = rear - mMaxValues + (mMaxValues >> 4);
74            mBytesOverrun += mFront - oldFront;
75            ++mOverruns;
76            return OVERRUN;
77        }
78        return avail;
79    }
80
81    // buffer must != null.
82    // offset must be >= 0.
83    // count is maximum number of bytes to copy, and must be >= 0.
84    // offset + count must be <= buffer.length.
85    // Returns actual number of bytes copied >= 0.
86    public int read(short[] buffer, int offset, int count)
87    {
88        int avail = availableToRead();
89        if (avail <= 0) {
90            return avail;
91        }
92        // An overrun can occur from here on and be silently ignored,
93        // but it will be caught at next read()
94        if (count > avail) {
95            count = avail;
96        }
97        int front = mFront & (mMaxValues - 1);
98        int red = mMaxValues - front;
99        if (red > count) {
100            red = count;
101        }
102        // In particular, an overrun during the System.arraycopy will result in reading corrupt data
103        System.arraycopy(mBuffer, front, buffer, offset, red);
104        // We could re-read the rear pointer here to detect the corruption, but why bother?
105        if (front + red == mMaxValues) {
106            if ((count -= red) > front) {
107                count = front;
108            }
109            if (count > 0) {
110                System.arraycopy(mBuffer, 0, buffer, offset + red, count);
111                red += count;
112            }
113        }
114        mFront += red;
115        return red;
116    }
117
118    public void flush()
119    {
120        mRear = mFront;
121        mVolatileRear = mFront;
122    }
123
124    // Round up to the next highest power of 2
125    private static int roundup(int v)
126    {
127        // Integer.numberOfLeadingZeros() returns 32 for zero input
128        if (v == 0) {
129            v = 1;
130        }
131        int lz = Integer.numberOfLeadingZeros(v);
132        int rounded = 0x80000000 >>> lz;
133        // 0x800000001 and higher are actually rounded _down_ to prevent overflow
134        if (v > rounded && lz > 0) {
135            rounded <<= 1;
136        }
137        return rounded;
138    }
139
140}
141