blob: 7638d912e391b87f9f02e46d9f749023416e32dc [file] [log] [blame]
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
#include "signal/src/circular_buffer.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define ASSERT assert
namespace tflite {
namespace tflm_signal {
// TODO(b/286250473): remove namespace once de-duped libraries above
void CircularBufferReset(tflm_signal::CircularBuffer* cb) {
cb->read = 0;
cb->write = 0;
cb->empty = 1;
cb->buffer = (int16_t*)(cb + 1);
memset(cb->buffer, 0, sizeof(cb->buffer[0]) * cb->buffer_size);
}
size_t CircularBufferGetNeededMemory(size_t capacity) {
return sizeof(CircularBuffer) + sizeof(int16_t) * 2 * capacity;
}
CircularBuffer* CircularBufferInit(size_t capacity, void* state,
size_t state_size) {
ASSERT(CircularBufferGetNeededMemory(capacity) >= state_size);
CircularBuffer* cb = (CircularBuffer*)state;
cb->buffer_size = 2 * capacity;
cb->capacity = capacity;
CircularBufferReset(cb);
return cb;
}
size_t CircularBufferCapacity(const tflm_signal::CircularBuffer* cb) {
return cb->capacity;
}
bool CircularBufferFull(const tflm_signal::CircularBuffer* cb) {
return cb->read == cb->write && cb->empty == 0;
}
bool CircularBufferEmpty(const tflm_signal::CircularBuffer* cb) {
return cb->empty == 1;
}
size_t CircularBufferAvailable(const tflm_signal::CircularBuffer* cb) {
const int32_t diff = cb->write - cb->read;
if (diff > 0) {
return diff;
} else if (diff < 0) {
return cb->capacity + diff;
} else if (cb->empty == 1) {
return 0;
} else {
return cb->capacity;
}
}
size_t CircularBufferCanWrite(const tflm_signal::CircularBuffer* cb) {
return cb->capacity - CircularBufferAvailable(cb);
}
void CircularBufferAdd(tflm_signal::CircularBuffer* cb, int16_t value) {
ASSERT(!CircularBufferFull(cb));
cb->buffer[cb->write] = value;
cb->buffer[cb->write + cb->capacity] = value;
if (++cb->write == cb->capacity) {
cb->write = 0;
}
cb->empty = 0;
}
void CircularBufferWrite(tflm_signal::CircularBuffer* cb, const int16_t* values,
size_t n) {
if (n > 0) {
ASSERT(CircularBufferCanWrite(cb) >= n);
size_t write = cb->write;
int16_t* buffer = cb->buffer;
const size_t capacity = cb->capacity;
const size_t end = write + n;
memcpy(buffer + write, values, n * sizeof(int16_t));
if (end < capacity) {
memcpy(buffer + capacity + write, values, n * sizeof(int16_t));
write += n;
} else {
const size_t n1 = capacity - write;
const size_t nbytes1 = n1 * sizeof(int16_t);
memcpy(buffer + capacity + write, values, nbytes1);
const size_t n2 = end - capacity;
if (n2 > 0) {
const size_t nbytes2 = n2 * sizeof(int16_t);
memcpy(buffer, values + n1, nbytes2);
}
write = n2;
}
cb->write = write;
cb->empty = 0;
}
}
void CircularBufferWriteZeros(tflm_signal::CircularBuffer* cb, size_t n) {
if (n > 0) {
ASSERT(CircularBufferCanWrite(cb) >= n);
size_t write = cb->write;
int16_t* buffer = cb->buffer;
const size_t capacity = cb->capacity;
const size_t end = write + n;
memset(buffer + write, 0, n * sizeof(int16_t));
if (end < capacity) {
memset(buffer + capacity + write, 0, n * sizeof(int16_t));
write += n;
} else {
const size_t n1 = capacity - write;
const size_t nbytes1 = n1 * sizeof(int16_t);
memset(buffer + capacity + write, 0, nbytes1);
const size_t n2 = end - capacity;
if (n2 > 0) {
const size_t nbytes2 = n2 * sizeof(int16_t);
memset(buffer, 0, nbytes2);
}
write = n2;
}
cb->write = write;
cb->empty = 0;
}
}
int16_t* CircularBufferReserveForWrite(tflm_signal::CircularBuffer* cb,
size_t n) {
ASSERT(cb->write + n <= cb->capacity);
int16_t* write_ptr = cb->buffer + cb->write;
cb->write += n;
if (cb->write == cb->capacity) {
cb->write = 0;
}
cb->empty = cb->empty && n == 0;
return write_ptr;
}
void CircularBufferExtend(tflm_signal::CircularBuffer* cb, size_t count,
int32_t n) {
if (n > 0 && count > 0) {
ASSERT(CircularBufferCanWrite(cb) >= count * n);
ASSERT(CircularBufferAvailable(cb) >= count);
const size_t capacity = cb->capacity;
// start pos of region to copy
const size_t start =
(count > cb->write) ? cb->write + capacity - count : cb->write - count;
const size_t end = start + count;
int i;
if (end <= capacity) {
// the source elements are contiguous
for (i = 0; i < n; ++i) {
CircularBufferWrite(cb, cb->buffer + start, count);
}
} else {
// the source elements wrap around the end of the buffer
for (i = 0; i < n; ++i) {
const size_t n1 = capacity - start;
const size_t n2 = count - n1;
CircularBufferWrite(cb, cb->buffer + start, n1);
CircularBufferWrite(cb, cb->buffer, n2);
}
}
}
// Note: no need to update empty flag
}
int16_t CircularBufferRemove(tflm_signal::CircularBuffer* cb) {
ASSERT(!CircularBufferEmpty(cb));
const int16_t result = cb->buffer[cb->read];
if (++cb->read == cb->capacity) {
cb->read = 0;
}
if (cb->read == cb->write) {
cb->empty = 1;
}
return result;
}
int16_t CircularBufferPeek(const tflm_signal::CircularBuffer* cb,
size_t index) {
ASSERT(CircularBufferAvailable(cb) > index);
size_t target = cb->read + index;
while (target >= cb->capacity) {
target -= cb->capacity;
}
return cb->buffer[target];
}
void CircularBufferRewind(tflm_signal::CircularBuffer* cb, size_t n) {
ASSERT(n <= CircularBufferCanWrite(cb));
if (n > cb->read) {
// Must add before subtracting because types are unsigned.
cb->read = (cb->read + cb->capacity) - n;
} else {
cb->read -= n;
}
if (n > 0) cb->empty = 0;
}
const int16_t* CircularBufferPeekDirect(const tflm_signal::CircularBuffer* cb,
size_t index) {
ASSERT(CircularBufferAvailable(cb) > index);
size_t target = cb->read + index;
while (target >= cb->capacity) {
target -= cb->capacity;
}
return cb->buffer + target;
}
const int16_t* CircularBufferPeekMax(const tflm_signal::CircularBuffer* cb,
size_t* n) {
if (CircularBufferAvailable(cb) > 0) {
*n = (cb->write <= cb->read) ? cb->capacity - cb->read
: cb->write - cb->read;
return cb->buffer + cb->read;
} else {
*n = 0;
return NULL;
}
}
void CircularBufferGet(tflm_signal::CircularBuffer* cb, size_t n,
int16_t* values) {
ASSERT(CircularBufferAvailable(cb) >= n);
const int16_t* buffer = cb->buffer;
const size_t read = cb->read;
const size_t end = read + n;
const size_t capacity = cb->capacity;
if (end <= capacity) {
memcpy(values, buffer + read, n * sizeof(int16_t));
} else {
const size_t n1 = capacity - read;
const size_t n2 = end - capacity;
const size_t nbytes1 = n1 * sizeof(int16_t);
const size_t nbytes2 = n2 * sizeof(int16_t);
memcpy(values, buffer + read, nbytes1);
memcpy(values + n1, buffer, nbytes2);
}
}
void CircularBufferDiscard(tflm_signal::CircularBuffer* cb, size_t n) {
ASSERT(n > 0);
ASSERT(CircularBufferAvailable(cb) >= n);
cb->read += n;
if (cb->read >= cb->capacity) {
cb->read -= cb->capacity;
}
if (cb->read == cb->write) {
cb->empty = 1;
}
}
void CircularBufferShift(tflm_signal::CircularBuffer* cb, int n) {
if (n < 0) {
ASSERT(-n <= (int)cb->capacity);
if ((int)cb->read < -n) {
// First add then subtract to ensure positivity as types are unsigned.
cb->read += cb->capacity;
}
cb->read += n;
} else {
ASSERT(n <= (int)cb->capacity);
cb->read += n;
if (cb->read >= cb->capacity) {
cb->read -= cb->capacity;
}
}
}
} // namespace tflm_signal
} // namespace tflite