/* * Sample Buffer - Allows reading and writing of timed samples * * Copyright 2010,2011 Free Software Foundation, Inc. * Copyright (C) 2015 Ettus Research LLC * Copyright 2019 sysmocom - s.f.m.c. GmbH * * Author: Tom Tsou * * SPDX-License-Identifier: AGPL-3.0+ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include "smpl_buf.h" #include smpl_buf::smpl_buf(size_t len) : buf_len(len), time_start(0), time_end(0), data_start(0), data_end(0) { data = new uint32_t[len]; } smpl_buf::~smpl_buf() { delete[] data; } ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const { if (timestamp < time_start) return ERROR_TIMESTAMP; else if (timestamp >= time_end) return 0; else return time_end - timestamp; } ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp) { int type_sz = 2 * sizeof(short); // Check for valid read if (timestamp < time_start) return ERROR_TIMESTAMP; if (timestamp >= time_end) return 0; if (len >= buf_len) return ERROR_READ; // How many samples should be copied size_t num_smpls = time_end - timestamp; if (num_smpls > len) num_smpls = len; // Starting index size_t read_start = (data_start + (timestamp - time_start)) % buf_len; // Read it if (read_start + num_smpls < buf_len) { size_t numBytes = len * type_sz; memcpy(buf, data + read_start, numBytes); } else { size_t first_cp = (buf_len - read_start) * type_sz; size_t second_cp = len * type_sz - first_cp; memcpy(buf, data + read_start, first_cp); memcpy((char*) buf + first_cp, data, second_cp); } data_start = (read_start + len) % buf_len; time_start = timestamp + len; if (time_start > time_end) return ERROR_READ; else return num_smpls; } ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp) { int type_sz = 2 * sizeof(short); // Check for valid write if ((len == 0) || (len >= buf_len)) return ERROR_WRITE; if ((timestamp + len) <= time_end) return ERROR_TIMESTAMP; if (timestamp < time_end) { LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp=" << timestamp << " time_end=" << time_end; // Do not return error here, because it's a rounding error and is not fatal } if (timestamp > time_end && time_end != 0) { LOGC(DDEV, ERR) << "Skipping buffer data: timestamp=" << timestamp << " time_end=" << time_end; // Do not return error here, because it's a rounding error and is not fatal } // Starting index size_t write_start = (data_start + (timestamp - time_start)) % buf_len; // Write it if ((write_start + len) < buf_len) { size_t numBytes = len * type_sz; memcpy(data + write_start, buf, numBytes); } else { size_t first_cp = (buf_len - write_start) * type_sz; size_t second_cp = len * type_sz - first_cp; memcpy(data + write_start, buf, first_cp); memcpy(data, (char*) buf + first_cp, second_cp); } data_end = (write_start + len) % buf_len; time_end = timestamp + len; if (!data_start) data_start = write_start; if (((write_start + len) > buf_len) && (data_end > data_start)) return ERROR_OVERFLOW; else if (time_end <= time_start) return ERROR_WRITE; else return len; } std::string smpl_buf::str_status(TIMESTAMP timestamp) const { std::ostringstream ost("Sample buffer: "); ost << "timestamp = " << timestamp; ost << ", length = " << buf_len; ost << ", time_start = " << time_start; ost << ", time_end = " << time_end; ost << ", data_start = " << data_start; ost << ", data_end = " << data_end; return ost.str(); } std::string smpl_buf::str_code(ssize_t code) { switch (code) { case ERROR_TIMESTAMP: return "Sample buffer: Requested timestamp is not valid"; case ERROR_READ: return "Sample buffer: Read error"; case ERROR_WRITE: return "Sample buffer: Write error"; case ERROR_OVERFLOW: return "Sample buffer: Overrun"; default: return "Sample buffer: Unknown error"; } }