#include "pxtbase.h" #include using namespace std; //% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte namespace BufferMethods { //% uint8_t *getBytes(Buffer buf) { return buf->data; } //% int getByte(Buffer buf, int off) { if (buf && 0 <= off && off < buf->length) return buf->data[off]; return 0; } //% void setByte(Buffer buf, int off, int v) { if (buf && 0 <= off && off < buf->length) buf->data[off] = v; } int writeBuffer(Buffer buf, int dstOffset, Buffer src, int srcOffset = 0, int length = -1) { if (length < 0) length = src->length; if (srcOffset < 0 || dstOffset < 0 || dstOffset > buf->length) return -1; length = min(src->length - srcOffset, buf->length - dstOffset); if (length < 0) return -1; if (buf == src) { memmove(buf->data + dstOffset, src->data + srcOffset, length); } else { memcpy(buf->data + dstOffset, src->data + srcOffset, length); } return 0; } /** * Write a number in specified format in the buffer. */ //% void setNumber(Buffer buf, NumberFormat format, int offset, TNumber value) { if (offset < 0) return; setNumberCore(buf->data + offset, buf->length - offset, format, value); } /** * Read a number in specified format from the buffer. */ //% TNumber getNumber(Buffer buf, NumberFormat format, int offset) { if (offset < 0) return fromInt(0); return getNumberCore(buf->data + offset, buf->length - offset, format); } /** Returns the length of a Buffer object. */ //% property int length(Buffer s) { return s->length; } /** * Fill (a fragment) of the buffer with given value. */ //% void fill(Buffer buf, int value, int offset = 0, int length = -1) { if (offset < 0 || offset > buf->length) return; // DEVICE_INVALID_PARAMETER; if (length < 0) length = buf->length; length = min(length, buf->length - offset); memset(buf->data + offset, value, length); } /** * Return a copy of a fragment of a buffer. */ //% Buffer slice(Buffer buf, int offset = 0, int length = -1) { offset = min((int)buf->length, offset); if (length < 0) length = buf->length; length = min(length, buf->length - offset); return mkBuffer(buf->data + offset, length); } /** * Shift buffer left in place, with zero padding. * @param offset number of bytes to shift; use negative value to shift right * @param start start offset in buffer. Default is 0. * @param length number of elements in buffer. If negative, length is set as the buffer length minus * start. eg: -1 */ //% void shift(Buffer buf, int offset, int start = 0, int length = -1) { if (length < 0) length = buf->length - start; if (start < 0 || start + length > buf->length || start + length < start || length == 0 || offset == 0 || offset == INT_MIN) return; if (offset <= -length || offset >= length) { fill(buf, 0); return; } uint8_t *data = buf->data + start; if (offset < 0) { offset = -offset; memmove(data + offset, data, length - offset); memset(data, 0, offset); } else { length = length - offset; memmove(data, data + offset, length); memset(data + length, 0, offset); } } /** * Convert a buffer to its hexadecimal representation. */ //% String toHex(Buffer buf) { const char *hex = "0123456789abcdef"; auto res = mkString(NULL, buf->length * 2); for (int i = 0; i < buf->length; ++i) { res->data[i << 1] = hex[buf->data[i] >> 4]; res->data[(i << 1) + 1] = hex[buf->data[i] & 0xf]; } return res; } /** * Rotate buffer left in place. * @param offset number of bytes to shift; use negative value to shift right * @param start start offset in buffer. Default is 0. * @param length number of elements in buffer. If negative, length is set as the buffer length minus * start. eg: -1 */ //% void rotate(Buffer buf, int offset, int start = 0, int length = -1) { if (length < 0) length = buf->length - start; if (start < 0 || start + length > buf->length || start + length < start || length == 0 || offset == 0 || offset == INT_MIN) return; if (offset < 0) offset += length << 8; // try to make it positive offset %= length; if (offset < 0) offset += length; uint8_t *data = buf->data + start; uint8_t *n_first = data + offset; uint8_t *first = data; uint8_t *next = n_first; uint8_t *last = data + length; while (first != next) { uint8_t tmp = *first; *first++ = *next; *next++ = tmp; if (next == last) { next = n_first; } else if (first == n_first) { n_first = next; } } } /** * Write contents of `src` at `dstOffset` in current buffer. */ //% void write(Buffer buf, int dstOffset, Buffer src) { // srcOff and length not supported, we only do up to 4 args :/ writeBuffer(buf, dstOffset, src, 0, -1); } } namespace control { /** * Create a new zero-initialized buffer. * @param size number of bytes in the buffer */ //% Buffer createBuffer(int size) { return mkBuffer(NULL, size); } } namespace pxt { static int writeBytes(uint8_t *dst, uint8_t *src, int length, bool swapBytes, int szLeft) { if (szLeft < length) { return -1; } if (swapBytes) { uint8_t *p = dst + length; for (int i = 0; i < length; ++i) *--p = src[i]; } else { if (length == 4 && ((uint32_t)dst & 3) == 0) *(uint32_t *)dst = *(uint32_t *)src; else if (length == 2 && ((uint32_t)dst & 1) == 0) *(uint16_t *)dst = *(uint16_t *)src; else memcpy(dst, src, length); } return 0; } static int readBytes(uint8_t *src, uint8_t *dst, int length, bool swapBytes, int szLeft) { if (szLeft < length) { memset(dst, 0, length); return -1; } if (swapBytes) { uint8_t *p = src + length; for (int i = 0; i < length; ++i) dst[i] = *--p; } else { if (length == 4 && ((uint32_t)src & 3) == 0) *(uint32_t *)dst = *(uint32_t *)src; else if (length == 2 && ((uint32_t)src & 1) == 0) *(uint16_t *)dst = *(uint16_t *)src; else memcpy(dst, src, length); } return 0; } void setNumberCore(uint8_t *buf, int szLeft, NumberFormat format, TNumber value) { int8_t i8; uint8_t u8; int16_t i16; uint16_t u16; int32_t i32; uint32_t u32; float f32; double f64; // Assume little endian #define WRITEBYTES(isz, swap, toInt) \ isz = toInt(value); \ writeBytes(buf, (uint8_t *)&isz, sizeof(isz), swap, szLeft); \ break switch (format) { case NumberFormat::Int8LE: WRITEBYTES(i8, false, toInt); case NumberFormat::UInt8LE: WRITEBYTES(u8, false, toInt); case NumberFormat::Int16LE: WRITEBYTES(i16, false, toInt); case NumberFormat::UInt16LE: WRITEBYTES(u16, false, toInt); case NumberFormat::Int32LE: WRITEBYTES(i32, false, toInt); case NumberFormat::UInt32LE: WRITEBYTES(u32, false, toUInt); case NumberFormat::Int8BE: WRITEBYTES(i8, true, toInt); case NumberFormat::UInt8BE: WRITEBYTES(u8, true, toInt); case NumberFormat::Int16BE: WRITEBYTES(i16, true, toInt); case NumberFormat::UInt16BE: WRITEBYTES(u16, true, toInt); case NumberFormat::Int32BE: WRITEBYTES(i32, true, toInt); case NumberFormat::UInt32BE: WRITEBYTES(u32, true, toUInt); case NumberFormat::Float32LE: WRITEBYTES(f32, false, toFloat); case NumberFormat::Float32BE: WRITEBYTES(f32, true, toFloat); case NumberFormat::Float64LE: WRITEBYTES(f64, false, toDouble); case NumberFormat::Float64BE: WRITEBYTES(f64, true, toDouble); } } TNumber getNumberCore(uint8_t *buf, int szLeft, NumberFormat format) { int8_t i8; uint8_t u8; int16_t i16; uint16_t u16; int32_t i32; uint32_t u32; float f32; double f64; // Assume little endian #define READBYTES(isz, swap, conv) \ readBytes(buf, (uint8_t *)&isz, sizeof(isz), swap, szLeft); \ return conv(isz) switch (format) { case NumberFormat::Int8LE: READBYTES(i8, false, fromInt); case NumberFormat::UInt8LE: READBYTES(u8, false, fromInt); case NumberFormat::Int16LE: READBYTES(i16, false, fromInt); case NumberFormat::UInt16LE: READBYTES(u16, false, fromInt); case NumberFormat::Int32LE: READBYTES(i32, false, fromInt); case NumberFormat::UInt32LE: READBYTES(u32, false, fromUInt); case NumberFormat::Int8BE: READBYTES(i8, true, fromInt); case NumberFormat::UInt8BE: READBYTES(u8, true, fromInt); case NumberFormat::Int16BE: READBYTES(i16, true, fromInt); case NumberFormat::UInt16BE: READBYTES(u16, true, fromInt); case NumberFormat::Int32BE: READBYTES(i32, true, fromInt); case NumberFormat::UInt32BE: READBYTES(u32, true, fromUInt); case NumberFormat::Float32LE: READBYTES(f32, false, fromFloat); case NumberFormat::Float32BE: READBYTES(f32, true, fromFloat); case NumberFormat::Float64LE: READBYTES(f64, false, fromDouble); case NumberFormat::Float64BE: READBYTES(f64, true, fromDouble); } return 0; } }