1/*
2 * Copyright (C) 2012 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 org.drrickorang.loopback;
18
19
20/**
21 * Non-blocking pipe where writer writes to the pipe using write() and read reads from the pipe
22 * using read(). Data in the pipe are stored in the short array "mBuffer".
23 * The write side of a pipe permits overruns; flow control is the caller's responsibility.
24 */
25
26public class PipeShort extends Pipe {
27    private int          mFront; // writer's current position
28    private int          mRear; // reader's current position
29    private final short  mBuffer[]; // store that data in the pipe
30    private volatile int mVolatileRear; // used to keep rear synchronized
31
32
33    /**
34     * IMPORTANT: Since a signed integer is used to store mRear and mFront, their values should not
35     * exceed 2^31 - 1, or else overflows happens and the positions of read and mFront becomes
36     * incorrect.
37     */
38    public PipeShort(int maxSamples) {
39        super(maxSamples);
40        mBuffer = new short[mMaxValues];
41    }
42
43
44    /**
45     * offset must be >= 0.
46     * count is maximum number of bytes to copy, and must be >= 0.
47     * offset + count must be <= buffer.length.
48     * Return actual number of shorts copied, which will be >= 0.
49     */
50    public int write(short[] buffer, int offset, int count) {
51        // mask the upper bits to get the correct position in the pipe
52        int rear = mRear & (mMaxValues - 1);
53        int written = mMaxValues - rear;
54        if (written > count) {
55            written = count;
56        }
57
58        System.arraycopy(buffer, offset, mBuffer, rear, written);
59        if (rear + written == mMaxValues) {
60            if ((count -= written) > rear) {
61                count = rear;
62            }
63            if (count > 0) {
64                System.arraycopy(buffer, offset + written, mBuffer, 0, count);
65                written += count;
66            }
67        }
68
69        mRear += written;
70        mVolatileRear = mRear;
71        return written;
72    }
73
74
75    @Override
76    public int read(short[] buffer, int offset, int count) {
77        int avail = availableToRead();
78        if (avail <= 0) {
79            return avail;
80        }
81
82        // An overrun can occur from here on and be silently ignored,
83        // but it will be caught at next read()
84        if (count > avail) {
85            count = avail;
86        }
87
88        // mask the upper bits to get the correct position in the pipe
89        int front = mFront & (mMaxValues - 1);
90        int read = mMaxValues - front;
91
92        if (read > count) {
93            read = count;
94        }
95
96        // In particular, an overrun during the System.arraycopy will result in reading corrupt data
97        System.arraycopy(mBuffer, front, buffer, offset, read);
98        // We could re-read the rear pointer here to detect the corruption, but why bother?
99        if (front + read == mMaxValues) {
100            if ((count -= read) > front) {
101                count = front;
102            }
103
104            if (count > 0) {
105                System.arraycopy(mBuffer, 0, buffer, offset + read, count);
106                read += count;
107            }
108        }
109
110        mFront += read;
111        return read;
112    }
113
114
115
116    @Override
117    public int availableToRead() {
118        int rear = mVolatileRear;
119        int avail = rear - mFront;
120        if (avail > mMaxValues) {
121            // Discard 1/16 of the most recent data in pipe to avoid another overrun immediately
122            int oldFront = mFront;
123            mFront = rear - mMaxValues + (mMaxValues >> 4);
124            mSamplesOverrun += mFront - oldFront;
125            ++mOverruns;
126            return OVERRUN;
127        }
128
129        return avail;
130    }
131
132
133    @Override
134    public void flush() {
135        mRear = mFront;
136        mVolatileRear = mFront;
137    }
138
139}
140