SatCat5
io_readable.cc
1 // Copyright 2023-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <cstring>
7 #include <satcat5/io_readable.h>
8 #include <satcat5/io_writeable.h>
9 #include <satcat5/utils.h>
10 
11 // Set batch size for copy_to()
12 #ifndef SATCAT5_BUFFCOPY_BATCH
13 #define SATCAT5_BUFFCOPY_BATCH 32
14 #endif
15 
27 
28 // Global instance of the basic NullRead and NullSink objects.
31 
32 #if SATCAT5_ALLOW_DELETION
33 Readable::~Readable()
34 {
35  if (m_callback) m_callback->data_unlink(this);
36 }
37 #endif
38 
40  m_callback = callback;
42 }
43 
45  if (get_read_ready() >= 1) {
46  return read_next();
47  } else {
49  return 0;
50  }
51 }
52 
53 // Make sure Doxygen ignores all read_ functions for scalars except read_u8().
55 u16 Readable::read_u16() {
56  if (get_read_ready() >= 2) {
57  u16 temp = read_next(); // Big-endian
58  return (temp << 8) | read_next();
59  } else {
61  return 0;
62  }
63 }
64 
65 u32 Readable::read_u24() {
66  if (get_read_ready() >= 3) {
67  u32 temp = read_next(); // Big-endian
68  temp = (temp << 8) | read_next();
69  return (temp << 8) | read_next();
70  } else {
72  return 0;
73  }
74 }
75 
76 u32 Readable::read_u32() {
77  if (get_read_ready() >= 4) {
78  u32 temp = read_next(); // Big-endian
79  temp = (temp << 8) | read_next();
80  temp = (temp << 8) | read_next();
81  return (temp << 8) | read_next();
82  } else {
84  return 0;
85  }
86 }
87 
88 u64 Readable::read_u48() {
89  if (get_read_ready() >= 6) {
90  u64 temp = read_next(); // Big-endian
91  temp = (temp << 8) | read_next();
92  temp = (temp << 8) | read_next();
93  temp = (temp << 8) | read_next();
94  temp = (temp << 8) | read_next();
95  return (temp << 8) | read_next();
96  } else {
98  return 0;
99  }
100 }
101 
102 u64 Readable::read_u64() {
103  if (get_read_ready() >= 8) {
104  u64 temp = read_next(); // Big-endian
105  temp = (temp << 8) | read_next();
106  temp = (temp << 8) | read_next();
107  temp = (temp << 8) | read_next();
108  temp = (temp << 8) | read_next();
109  temp = (temp << 8) | read_next();
110  temp = (temp << 8) | read_next();
111  return (temp << 8) | read_next();
112  } else {
113  read_underflow();
114  return 0;
115  }
116 }
117 
118 s8 Readable::read_s8()
119  { return reinterpret<u8, s8>(read_u8()); }
120 
121 s16 Readable::read_s16()
122  { return reinterpret<u16, s16>(read_u16()); }
123 
124 s32 Readable::read_s24() // Sign extension required
125  { return (reinterpret<u32, s32>(read_u24()) << 8) >> 8; }
126 
127 s32 Readable::read_s32()
128  { return reinterpret<u32, s32>(read_u32()); }
129 
130 s64 Readable::read_s48() // Sign extension required
131  { return (reinterpret<u64, s64>(read_u48()) << 16) >> 16; }
132 
133 s64 Readable::read_s64()
134  { return reinterpret<u64, s64>(read_u64()); }
135 
136 float Readable::read_f32()
137  { return reinterpret<u32, float>(read_u32()); }
138 
139 double Readable::read_f64()
140  { return reinterpret<u64, double>(read_u64()); }
141 
142 u16 Readable::read_u16l()
143  { return __builtin_bswap16(read_u16()); }
144 
145 u32 Readable::read_u24l()
146  { return __builtin_bswap32(read_u24()) >> 8; }
147 
148 u32 Readable::read_u32l()
149  { return __builtin_bswap32(read_u32()); }
150 
151 u64 Readable::read_u48l()
152  { return __builtin_bswap64(read_u48()) >> 16; }
153 
154 u64 Readable::read_u64l()
155  { return __builtin_bswap64(read_u64()); }
156 
157 s16 Readable::read_s16l()
158  { return reinterpret<u16, s16>(read_u16l()); }
159 
160 s32 Readable::read_s24l() // Sign extension required
161  { return reinterpret<u32, s32>(__builtin_bswap32(read_u24())) >> 8; }
162 
163 s32 Readable::read_s32l()
164  { return reinterpret<u32, s32>(read_u32l()); }
165 
166 s64 Readable::read_s48l() // Sign extension required
167  { return reinterpret<u64, s64>(__builtin_bswap64(read_u48())) >> 16; }
168 
169 s64 Readable::read_s64l()
170  { return reinterpret<u64, s64>(read_u64l()); }
171 
172 float Readable::read_f32l()
173  { return reinterpret<u32, float>(read_u32l()); }
174 
175 double Readable::read_f64l()
176  { return reinterpret<u64, double>(read_u64l()); }
178 
179 unsigned Readable::read_str(unsigned dst_size, char* dst) {
180  unsigned nwrite = 0;
181  while (get_read_ready() > 0) { // Stop at end-of-input?
182  u8 tmp = read_next(); // Read next byte.
183  if (tmp == 0) break; // Null-termination?
184  if (nwrite+1 < dst_size) dst[nwrite++] = (char)tmp;
185  }
186  dst[nwrite] = 0; // Always null-terminate
187  return nwrite;
188 }
189 
190 bool Readable::read_bytes(unsigned nbytes, void* dst) {
191  u8* dst_u8 = (u8*)dst;
192  if (get_read_ready() >= nbytes) {
193  while (nbytes) {
194  *dst_u8 = read_next();
195  ++dst_u8; --nbytes;
196  }
197  return true;
198  } else {
199  read_underflow();
200  return false;
201  }
202 }
203 
204 bool Readable::read_consume(unsigned nbytes) {
205  if (get_read_ready() >= nbytes) {
206  while (nbytes--) read_next();
207  return true;
208  } else {
209  read_underflow();
210  return false;
211  }
212 }
213 
214 unsigned Readable::copy_to(Writeable* dst) {
215  // Temporary buffer sets our maximum batch size.
216  u8 buff[SATCAT5_BUFFCOPY_BATCH];
217  unsigned total = 0;
218  while (1) {
219  // How much data could we copy from source to sink?
220  unsigned max_rd = get_read_ready();
221  unsigned max_wr = min_unsigned(max_rd, dst->get_write_space());
222  if (max_wr == 0) break;
223  // Copy up to that limit or batch size, whichever is smaller.
224  unsigned batch = min_unsigned(max_wr, SATCAT5_BUFFCOPY_BATCH);
225  read_bytes(batch, buff);
226  dst->write_bytes(batch, buff);
227  total += batch;
228  // Did we just finish a frame?
229  if (batch == max_rd) break;
230  }
231  return total;
232 }
233 
235  // Copy as much data as possible from the source.
236  unsigned count = copy_to(dst);
237  // Mode determines when to call read_finalize() and write_finalize().
238  bool rdf = false, wrf = false;
239  if (mode == CopyMode::STREAM) {
240  rdf = false;
241  wrf = (count > 0);
242  } else if (mode == CopyMode::PACKET) {
243  rdf = (count > 0) && !get_read_ready();
244  wrf = (count > 0) && !get_read_ready();
245  } else if (mode == CopyMode::ALWAYS) {
246  rdf = !get_read_ready();
247  wrf = true;
248  }
249  // Apply selected policy.
250  if (rdf) read_finalize();
251  return wrf && dst->write_finalize();
252 }
253 
255  // If we have any data waiting, deliver it.
256  if (m_callback && get_read_ready() > 0) {
257  m_callback->data_rcvd(this);
258  }
259 }
260 
262  // If we have any data waiting, deliver it.
263  // If we STILL have data afterward, try again later.
264  if (m_callback && get_read_ready() > 0) {
265  m_callback->data_rcvd(this);
266  if (get_read_ready()) request_poll();
267  }
268 }
269 
272 
273 unsigned ArrayRead::get_read_ready() const {
274  return m_len - m_rdidx;
275 }
276 
277 void ArrayRead::read_reset(unsigned len) {
278  m_len = m_src ? len : 0;
279  m_rdidx = 0;
280 }
281 
283  return m_src[m_rdidx++];
284 }
285 
287  m_rdidx = 0;
288 }
289 
291  : m_src(src), m_rem(src->get_read_ready()) {}
292 
294  {return min_unsigned(m_rem, m_src->get_read_ready());}
295 
296 bool LimitedRead::read_bytes(unsigned nbytes, void* dst) {
297  if (nbytes <= m_rem) {
298  m_rem -= nbytes;
299  return m_src->read_bytes(nbytes, dst);
300  } else {
301  m_rem = 0;
302  return false;
303  }
304 
305 }
306 
307 bool LimitedRead::read_consume(unsigned nbytes) {
308  if (nbytes <= m_rem) {
309  m_rem -= nbytes;
310  return m_src->read_consume(nbytes);
311  } else {
312  m_rem = 0;
313  return false;
314  }
315 }
316 
318  read_consume(m_rem);
319 }
320 
322  // Internal method, parent has already checked get_read_ready()
323  --m_rem;
324  return m_src->read_next();
325 }
326 
327 // The ReadableRedirect class is all one-liners that could all be defined
328 // in the .h file. However, this leads to duplication of the underlying
329 // function (inline, direct, and virtual methods) that complicate testing.
330 // Defining these micro-functions here prevents such undesired changes.
332  Readable::set_callback(callback);
333  if (m_src) m_src->set_callback(callback);
334 }
335 
337  { return m_src ? m_src->get_read_ready() : 0; }
338 
339 bool ReadableRedirect::read_bytes(unsigned nbytes, void* dst)
340  { return m_src && m_src->read_bytes(nbytes, dst); }
341 
342 bool ReadableRedirect::read_consume(unsigned nbytes)
343  { return m_src && m_src->read_consume(nbytes); }
344 
346  { if (m_src) m_src->read_finalize(); }
347 
348 u8 ReadableRedirect::read_next() // Unreachable if get_read_ready() is zero.
349  { return m_src->read_next(); } // Therefore m_src cannot be null.
350 
352  { if (m_src) m_src->read_underflow(); }
353 
354 unsigned NullRead::get_read_ready() const
355  { return 0; } // The null source never generates data.
356 
357 // Unreachable because get_read_ready() is always zero.
358 u8 NullRead::read_next() // GCOVR_EXCL_LINE
359  { return 0; } // GCOVR_EXCL_LINE
360 
362  src->read_consume(src->get_read_ready());
363  src->read_finalize();
364 }
Ephemeral Readable interface for a simple array.
Definition: io_readable.h:206
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:273
u8 read_next() override
Read the next byte from the underlying buffer or device.
Definition: io_readable.cc:282
void read_reset(unsigned len)
Reset read position to the start of the backing array, and set the readable length to the specified v...
Definition: io_readable.cc:277
void read_finalize() override
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:286
Event-handler interface for newly received data.
Definition: io_readable.h:43
virtual void data_unlink(satcat5::io::Readable *src)
Unlink this EventListener from the designated source, because the designated Readable object is being...
Definition: io_readable.h:54
virtual void data_rcvd(satcat5::io::Readable *src)=0
The data_rcvd() callback is polled whenever data is available.
Limited read of next N bytes.
Definition: io_readable.h:255
bool read_consume(unsigned nbytes) override
Read and discard 0 or more bytes.
Definition: io_readable.cc:307
constexpr LimitedRead(satcat5::io::Readable *src, unsigned maxrd)
Explicitly set maximum read length in bytes.
Definition: io_readable.h:261
u8 read_next() override
Read the next byte from the underlying buffer or device.
Definition: io_readable.cc:321
bool read_bytes(unsigned nbytes, void *dst) override
Read 0 or more bytes into a buffer.
Definition: io_readable.cc:296
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:293
void read_finalize() override
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:317
Readable object that never produces any data.
Definition: io_readable.h:327
u8 read_next() override
Read the next byte from the underlying buffer or device.
Definition: io_readable.cc:358
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:354
An EventListener object that immediately discards all received data.
Definition: io_readable.h:336
void data_rcvd(satcat5::io::Readable *src) override
The data_rcvd() callback is polled whenever data is available.
Definition: io_readable.cc:361
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
virtual void read_underflow()
Optional error handling for read underflow.
Definition: io_readable.cc:271
void poll_demand()
Event handler for on-demand polling.
Definition: io_readable.cc:261
unsigned read_str(unsigned dst_size, char *dst)
Safely read a null-terminated input string.
Definition: io_readable.cc:179
u8 read_u8()
One of many functions for reading integer/floating point values, see details.
Definition: io_readable.cc:44
virtual bool read_bytes(unsigned nbytes, void *dst)
Read 0 or more bytes into a buffer.
Definition: io_readable.cc:190
void read_notify()
Attempt notification by calling m_callback->data_rcvd().
Definition: io_readable.cc:254
virtual void read_finalize()
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:270
virtual bool read_consume(unsigned nbytes)
Read and discard 0 or more bytes.
Definition: io_readable.cc:204
virtual u8 read_next()=0
Read the next byte from the underlying buffer or device.
bool copy_and_finalize(satcat5::io::Writeable *dst, satcat5::io::CopyMode mode=CopyMode::PACKET)
Copy data to a Writeable object, then finalize.
Definition: io_readable.cc:234
virtual void set_callback(satcat5::io::EventListener *callback)
Update registered callback for data_rcvd() events.
Definition: io_readable.cc:39
unsigned copy_to(satcat5::io::Writeable *dst)
Copy data to a Writeable object, without finalizing.
Definition: io_readable.cc:214
satcat5::io::EventListener * m_callback
Pointer to the callback object, or NULL.
Definition: io_readable.h:199
virtual unsigned get_read_ready() const =0
How many bytes can be read without blocking?
Wrapper class for forwarding reads to another object.
Definition: io_readable.h:299
void read_finalize() override
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:345
bool read_bytes(unsigned nbytes, void *dst) override
Read 0 or more bytes into a buffer.
Definition: io_readable.cc:339
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:336
u8 read_next() override
Read the next byte from the underlying buffer or device.
Definition: io_readable.cc:348
void read_underflow() override
Optional error handling for read underflow.
Definition: io_readable.cc:351
void set_callback(satcat5::io::EventListener *callback) override
Update registered callback for data_rcvd() events.
Definition: io_readable.cc:331
bool read_consume(unsigned nbytes) override
Read and discard 0 or more bytes.
Definition: io_readable.cc:342
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual unsigned get_write_space() const =0
How many bytes can be written without blocking?
virtual void write_bytes(unsigned nbytes, const void *src)
Write 0 or more bytes from a buffer.
virtual bool write_finalize()
Mark end of frame and release temporary working data.
void request_poll()
Call this method to request polling at a later time.
Definition: polling.cc:208
"Readable" I/O interface core definitions
CopyMode
Specify read and write behavior for Readable::copy_and_finalize().
Definition: io_readable.h:23
satcat5::io::NullRead null_read
Global instances of the basic NullRead and NullSink objects.
Definition: io_readable.cc:29
satcat5::io::NullSink null_sink
Global instances of the basic NullRead and NullSink objects.
Definition: io_readable.cc:30
"Writeable" I/O interface core definitions
Miscellaneous mathematical utility functions.
constexpr unsigned min_unsigned(unsigned a, unsigned b)
Min and max functions.
Definition: utils.h:111
T2 reinterpret(T1 x)
In-place byte-for-byte format conversion, aka "type-punning".
Definition: utils.h:414