diff options
-rw-r--r-- | docs/libs/gstreamer-libs-sections.txt | 17 | ||||
-rw-r--r-- | libs/gst/base/gstbytereader.c | 335 | ||||
-rw-r--r-- | libs/gst/base/gstbytereader.h | 28 | ||||
-rw-r--r-- | tests/check/libs/bytereader.c | 131 |
4 files changed, 498 insertions, 13 deletions
diff --git a/docs/libs/gstreamer-libs-sections.txt b/docs/libs/gstreamer-libs-sections.txt index ec0ee4cdb..44ed3e62d 100644 --- a/docs/libs/gstreamer-libs-sections.txt +++ b/docs/libs/gstreamer-libs-sections.txt @@ -445,9 +445,26 @@ gst_byte_reader_peek_float64_le gst_byte_reader_peek_float64_be gst_byte_reader_get_data +gst_byte_reader_dup_data gst_byte_reader_peek_data gst_byte_reader_masked_scan_uint32 + +gst_byte_reader_get_string +gst_byte_reader_get_string_utf8 + +gst_byte_reader_peek_string +gst_byte_reader_peek_string_utf8 + +gst_byte_reader_dup_string +gst_byte_reader_dup_string_utf8 +gst_byte_reader_dup_string_utf16 +gst_byte_reader_dup_string_utf32 + +gst_byte_reader_skip_string +gst_byte_reader_skip_string_utf8 +gst_byte_reader_skip_string_utf16 +gst_byte_reader_skip_string_utf32 </SECTION> <SECTION> diff --git a/libs/gst/base/gstbytereader.c b/libs/gst/base/gstbytereader.c index 40b138599..7ba435a25 100644 --- a/libs/gst/base/gstbytereader.c +++ b/libs/gst/base/gstbytereader.c @@ -28,13 +28,15 @@ /** * SECTION:gstbytereader - * @short_description: Reads different integer and floating point types from a memory buffer + * @short_description: Reads different integer, string and floating point + * types from a memory buffer * * #GstByteReader provides a byte reader that can read different integer and * floating point types from a memory buffer. It provides functions for reading * signed/unsigned, little/big endian integers of 8, 16, 24, 32 and 64 bits * and functions for reading little/big endian floating points numbers of - * 32 and 64 bits. + * 32 and 64 bits. It also provides functions to read NUL-terminated strings + * in various character encodings. */ /** @@ -1162,6 +1164,20 @@ GST_BYTE_READER_READ_FLOATS (64, double, DOUBLE); * * Since: 0.10.22 */ +gboolean +gst_byte_reader_get_data (GstByteReader * reader, guint size, + const guint8 ** val) +{ + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (val != NULL, FALSE); + + if (reader->byte + size > reader->size) + return FALSE; + + *val = reader->data + reader->byte; + reader->byte += size; + return TRUE; +} /** * gst_byte_reader_peek_data: @@ -1178,9 +1194,8 @@ GST_BYTE_READER_READ_FLOATS (64, double, DOUBLE); * * Since: 0.10.22 */ - gboolean -gst_byte_reader_get_data (GstByteReader * reader, guint size, +gst_byte_reader_peek_data (GstByteReader * reader, guint size, const guint8 ** val) { g_return_val_if_fail (reader != NULL, FALSE); @@ -1190,21 +1205,32 @@ gst_byte_reader_get_data (GstByteReader * reader, guint size, return FALSE; *val = reader->data + reader->byte; - reader->byte += size; return TRUE; } +/** + * gst_byte_reader_dup_data: + * @reader: a #GstByteReader instance + * @size: Size in bytes + * @val: Pointer to a #guint8 to store the result + * + * Returns a newly-allocated copy of the current data + * position if at least @size bytes are left and + * updates the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + * + * Since: 0.10.24 + */ gboolean -gst_byte_reader_peek_data (GstByteReader * reader, guint size, - const guint8 ** val) +gst_byte_reader_dup_data (GstByteReader * reader, guint size, guint8 ** val) { - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); + const guint8 *cval = NULL; - if (reader->byte + size > reader->size) + if (!gst_byte_reader_get_data (reader, size, &cval)) return FALSE; - *val = reader->data + reader->byte; + *val = g_memdup (cval, size); return TRUE; } @@ -1285,3 +1311,290 @@ gst_byte_reader_masked_scan_uint32 (GstByteReader * reader, guint32 mask, /* nothing found */ return -1; } + +#define GST_BYTE_READER_SCAN_STRING(bits) \ +static guint \ +gst_byte_reader_scan_string_utf##bits (GstByteReader * reader) \ +{ \ + guint len, off, max_len; \ + \ + max_len = (reader->size - reader->byte) / sizeof (guint##bits); \ + \ + /* need at least a single NUL terminator */ \ + if (max_len < 1) \ + return 0; \ + \ + len = 0; \ + off = reader->byte; \ + /* endianness does not matter if we are looking for a NUL terminator */ \ + while (GST_READ_UINT##bits##_LE (&reader->data[off]) != 0) { \ + ++len; \ + off += sizeof (guint##bits); \ + /* have we reached the end without finding a NUL terminator? */ \ + if (len == max_len) \ + return 0; \ + } \ + /* return size in bytes including the NUL terminator (hence the +1) */ \ + return (len + 1) * sizeof (guint##bits); \ +} + +#define GST_READ_UINT8_LE GST_READ_UINT8 +GST_BYTE_READER_SCAN_STRING (8); +#undef GST_READ_UINT8_LE +GST_BYTE_READER_SCAN_STRING (16); +GST_BYTE_READER_SCAN_STRING (32); + +#define GST_BYTE_READER_SKIP_STRING(bits) \ +gboolean \ +gst_byte_reader_skip_string_utf##bits (GstByteReader * reader) \ +{ \ + guint size; /* size in bytes including the terminator */ \ + \ + g_return_val_if_fail (reader != NULL, FALSE); \ + \ + size = gst_byte_reader_scan_string_utf##bits (reader); \ + reader->byte += size; \ + return (size > 0); \ +} + +/** + * gst_byte_reader_skip_string: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated string in the #GstByteReader instance, advancing + * the current position to the byte after the string. This will work for + * any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + * + * Since: 0.10.24 + */ +/** + * gst_byte_reader_skip_string_utf8: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated string in the #GstByteReader instance, advancing + * the current position to the byte after the string. This will work for + * any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + * + * Since: 0.10.24 + */ +GST_BYTE_READER_SKIP_STRING (8); + +/** + * gst_byte_reader_skip_string_utf16: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated UTF-16 string in the #GstByteReader instance, + * advancing the current position to the byte after the string. + * + * No input checking for valid UTF-16 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + * + * Since: 0.10.24 + */ +GST_BYTE_READER_SKIP_STRING (16); + +/** + * gst_byte_reader_skip_string_utf32: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated UTF-32 string in the #GstByteReader instance, + * advancing the current position to the byte after the string. + * + * No input checking for valid UTF-32 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + * + * Since: 0.10.24 + */ +GST_BYTE_READER_SKIP_STRING (32); + +/** + * gst_byte_reader_peek_string: + * @reader: a #GstByteReader instance + * @str: Pointer to a #gchar to store the result + * + * Returns a constant pointer to the current data position if there is + * a NUL-terminated string in the data (this could be just a NUL terminator). + * The current position will be maintained. This will work for any + * NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + * + * Since: 0.10.24 + */ +/** + * gst_byte_reader_peek_string_utf8: + * @reader: a #GstByteReader instance + * @str: Pointer to a #gchar to store the result + * + * Returns a constant pointer to the current data position if there is + * a NUL-terminated string in the data (this could be just a NUL terminator). + * The current position will be maintained. This will work for any + * NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + * + * Since: 0.10.24 + */ +gboolean +gst_byte_reader_peek_string_utf8 (GstByteReader * reader, const gchar ** str) +{ + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + if (gst_byte_reader_scan_string_utf8 (reader) > 0) { + *str = (const gchar *) (reader->data + reader->byte); + } else { + *str = NULL; + } + return (*str != NULL); +} + +/** + * gst_byte_reader_get_string_utf8: + * @reader: a #GstByteReader instance + * @str: Pointer to a #gchar to store the result + * + * Returns a constant pointer to the current data position if there is + * a NUL-terminated string in the data (this could be just a NUL terminator), + * advancing the current position to the byte after the string. This will work + * for any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be found, %FALSE otherwise. + * + * Since: 0.10.24 + */ +gboolean +gst_byte_reader_get_string_utf8 (GstByteReader * reader, const gchar ** str) +{ + guint size; /* size in bytes including the terminator */ + + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + size = gst_byte_reader_scan_string_utf8 (reader); + if (size == 0) { + *str = NULL; + return FALSE; + } + + *str = (const gchar *) (reader->data + reader->byte); + reader->byte += size; + return TRUE; +} + +#define GST_BYTE_READER_DUP_STRING(bits,type) \ +gboolean \ +gst_byte_reader_dup_string_utf##bits (GstByteReader * reader, type ** str) \ +{ \ + guint size; /* size in bytes including the terminator */ \ + \ + g_return_val_if_fail (reader != NULL, FALSE); \ + g_return_val_if_fail (str != NULL, FALSE); \ + \ + size = gst_byte_reader_scan_string_utf##bits (reader); \ + if (size == 0) { \ + *str = NULL; \ + return FALSE; \ + } \ + *str = g_memdup (reader->data + reader->byte, size); \ + reader->byte += size; \ + return TRUE; \ +} + +/** + * gst_byte_reader_dup_string_utf8: + * @reader: a #GstByteReader instance + * @str: address of a string pointer to store the result + * + * FIXME:Reads (copies) a NUL-terminated string in the #GstByteReader instance, + * advancing the current position to the byte after the string. This will work + * for any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be read into @str, %FALSE otherwise. The + * string put into @str must be freed with g_free() when no longer needed. + * + * Since: 0.10.24 + */ +GST_BYTE_READER_DUP_STRING (8, gchar); + +/** + * gst_byte_reader_dup_string_utf16: + * @reader: a #GstByteReader instance + * @str: address of a #guint16 pointer to store the result + * + * Returns a newly-allocated copy of the current data position if there is + * a NUL-terminated UTF-16 string in the data (this could be an empty string + * as well), and advances the current position. + * + * No input checking for valid UTF-16 is done. This function is endianness + * agnostic - you should not assume the UTF-16 characters are in host + * endianness. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Note: there is no peek or get variant of this function to ensure correct + * byte alignment of the UTF-16 string. + * + * Returns: %TRUE if a string could be read, %FALSE otherwise. The + * string put into @str must be freed with g_free() when no longer needed. + * + * Since: 0.10.24 + */ +GST_BYTE_READER_DUP_STRING (16, guint16); + +/** + * gst_byte_reader_dup_string_utf32: + * @reader: a #GstByteReader instance + * @str: address of a #guint32 pointer to store the result + * + * Returns a newly-allocated copy of the current data position if there is + * a NUL-terminated UTF-32 string in the data (this could be an empty string + * as well), and advances the current position. + * + * No input checking for valid UTF-32 is done. This function is endianness + * agnostic - you should not assume the UTF-32 characters are in host + * endianness. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Note: there is no peek or get variant of this function to ensure correct + * byte alignment of the UTF-32 string. + * + * Returns: %TRUE if a string could be read, %FALSE otherwise. The + * string put into @str must be freed with g_free() when no longer needed. + * + * Since: 0.10.24 + */ +GST_BYTE_READER_DUP_STRING (32, guint32); diff --git a/libs/gst/base/gstbytereader.h b/libs/gst/base/gstbytereader.h index 1570f763e..96c59167d 100644 --- a/libs/gst/base/gstbytereader.h +++ b/libs/gst/base/gstbytereader.h @@ -102,8 +102,32 @@ gboolean gst_byte_reader_peek_float32_be (GstByteReader *reader, gfloat *val); gboolean gst_byte_reader_peek_float64_le (GstByteReader *reader, gdouble *val); gboolean gst_byte_reader_peek_float64_be (GstByteReader *reader, gdouble *val); -gboolean gst_byte_reader_get_data (GstByteReader *reader, guint size, const guint8 **val); -gboolean gst_byte_reader_peek_data (GstByteReader *reader, guint size, const guint8 **val); +gboolean gst_byte_reader_dup_data (GstByteReader * reader, guint size, guint8 ** val); +gboolean gst_byte_reader_get_data (GstByteReader * reader, guint size, const guint8 ** val); +gboolean gst_byte_reader_peek_data (GstByteReader * reader, guint size, const guint8 ** val); + +#define gst_byte_reader_dup_string(reader,str) \ + gst_byte_reader_dup_string_utf8(reader,str) + +gboolean gst_byte_reader_dup_string_utf8 (GstByteReader * reader, gchar ** str); +gboolean gst_byte_reader_dup_string_utf16 (GstByteReader * reader, guint16 ** str); +gboolean gst_byte_reader_dup_string_utf32 (GstByteReader * reader, guint32 ** str); + +#define gst_byte_reader_skip_string(reader) \ + gst_byte_reader_skip_string_utf8(reader) + +gboolean gst_byte_reader_skip_string_utf8 (GstByteReader * reader); +gboolean gst_byte_reader_skip_string_utf16 (GstByteReader * reader); +gboolean gst_byte_reader_skip_string_utf32 (GstByteReader * reader); + +#define gst_byte_reader_get_string(reader,str) \ + gst_byte_reader_get_string_utf8(reader,str) + +#define gst_byte_reader_peek_string(reader,str) \ + gst_byte_reader_peek_string_utf8(reader,str) + +gboolean gst_byte_reader_get_string_utf8 (GstByteReader * reader, const gchar ** str); +gboolean gst_byte_reader_peek_string_utf8 (GstByteReader * reader, const gchar ** str); guint gst_byte_reader_masked_scan_uint32 (GstByteReader * reader, guint32 mask, diff --git a/tests/check/libs/bytereader.c b/tests/check/libs/bytereader.c index 607750cc7..97d7bc9b8 100644 --- a/tests/check/libs/bytereader.c +++ b/tests/check/libs/bytereader.c @@ -551,6 +551,135 @@ GST_START_TEST (test_scan) GST_END_TEST; +GST_START_TEST (test_string_funcs) +{ + GstByteReader reader, backup; + const gchar *s8; + guint32 *c32; + guint16 *c16; + gchar *c8; + guint8 data[200], *d; + guint i; + + /* fill half the buffer with a pattern */ + for (i = 0; i < 100; i++) + data[i] = i + 1; + + gst_byte_reader_init (&reader, data, 100); + + /* no NUL terminator, so these should all fail */ + fail_if (gst_byte_reader_get_string (&reader, &s8)); + fail_if (gst_byte_reader_get_string_utf8 (&reader, &s8)); + fail_if (gst_byte_reader_dup_string (&reader, &c8)); + fail_if (gst_byte_reader_dup_string_utf8 (&reader, &c8)); + fail_if (gst_byte_reader_skip_string (&reader)); + fail_if (gst_byte_reader_skip_string_utf8 (&reader)); + fail_if (gst_byte_reader_skip_string_utf16 (&reader)); + fail_if (gst_byte_reader_skip_string_utf32 (&reader)); + fail_if (gst_byte_reader_peek_string (&reader, &s8)); + fail_if (gst_byte_reader_peek_string_utf8 (&reader, &s8)); + fail_if (gst_byte_reader_dup_string_utf16 (&reader, &c16)); + fail_if (gst_byte_reader_dup_string_utf32 (&reader, &c32)); + + /* let's add a single NUL terminator */ + data[80] = '\0'; + backup = reader; + fail_if (gst_byte_reader_skip_string_utf32 (&reader)); + fail_if (gst_byte_reader_skip_string_utf16 (&reader)); + fail_if (gst_byte_reader_dup_string_utf16 (&reader, &c16)); + fail_if (gst_byte_reader_dup_string_utf32 (&reader, &c32)); + fail_unless (gst_byte_reader_skip_string (&reader)); + reader = backup; + fail_unless (gst_byte_reader_skip_string_utf8 (&reader)); + reader = backup; + fail_unless (gst_byte_reader_peek_string (&reader, &s8)); + fail_unless (gst_byte_reader_peek_string_utf8 (&reader, &s8)); + fail_if (gst_byte_reader_dup_string_utf16 (&reader, &c16)); + fail_if (gst_byte_reader_dup_string_utf32 (&reader, &c32)); + + /* let's add another NUL terminator */ + data[81] = '\0'; + reader = backup; + fail_if (gst_byte_reader_skip_string_utf32 (&reader)); + fail_if (gst_byte_reader_dup_string_utf32 (&reader, &c32)); + fail_unless (gst_byte_reader_skip_string_utf16 (&reader)); + reader = backup; + fail_unless (gst_byte_reader_dup_string_utf16 (&reader, &c16)); + g_free (c16); + reader = backup; + fail_unless (gst_byte_reader_skip_string (&reader)); + reader = backup; + fail_unless (gst_byte_reader_skip_string_utf8 (&reader)); + reader = backup; + fail_unless (gst_byte_reader_peek_string (&reader, &s8)); + fail_unless (gst_byte_reader_peek_string_utf8 (&reader, &s8)); + fail_if (gst_byte_reader_dup_string_utf32 (&reader, &c32)); + + /* two more NUL terminators */ + data[79] = '\0'; + data[82] = '\0'; + reader = backup; + /* we're at pos. 80 now, so have only 3 NUL terminators in front of us */ + fail_if (gst_byte_reader_skip_string_utf32 (&reader)); + /* let's rewind */ + gst_byte_reader_init (&reader, data, 100); + backup = reader; + /* oops, 79 is not dividable by 4, so not aligned, so should fail as well! */ + fail_if (gst_byte_reader_skip_string_utf32 (&reader)); + /* let's try that again */ + data[83] = '\0'; + gst_byte_reader_init (&reader, data, 100); + backup = reader; + fail_unless (gst_byte_reader_skip_string_utf16 (&reader)); + reader = backup; + fail_unless (gst_byte_reader_skip_string (&reader)); + reader = backup; + fail_unless (gst_byte_reader_skip_string_utf8 (&reader)); + reader = backup; + fail_unless (gst_byte_reader_peek_string (&reader, &s8)); + fail_unless (gst_byte_reader_peek_string_utf8 (&reader, &s8)); + fail_unless (gst_byte_reader_dup_string_utf16 (&reader, &c16)); + g_free (c16); + reader = backup; + fail_unless (gst_byte_reader_dup_string_utf32 (&reader, &c32)); + g_free (c32); + + /* and again from the start */ + gst_byte_reader_init (&reader, data, 100); + fail_unless (gst_byte_reader_skip_string_utf16 (&reader)); + fail_if (gst_byte_reader_dup_data (&reader, 200, &d)); + fail_if (gst_byte_reader_dup_data (&reader, 100, &d)); + fail_if (gst_byte_reader_dup_data (&reader, 20, &d)); + fail_unless (gst_byte_reader_dup_data (&reader, 10, &d)); + fail_unless_equals_int (d[0], 0); + fail_unless_equals_int (d[1], 0); + fail_unless_equals_int (d[2], 85); + fail_unless_equals_int (d[3], 86); + g_free (d); +} + +GST_END_TEST; + +GST_START_TEST (test_dup_string) +{ + const gchar moredata[] = { 0x99, 0x10, 'f', '0', '0', '!', '\0', 0xff }; + GstByteReader reader; + guint16 num; + guint8 x; + gchar *s; + + gst_byte_reader_init (&reader, (guint8 *) moredata, sizeof (moredata)); + fail_unless (gst_byte_reader_get_uint16_be (&reader, &num)); + fail_unless_equals_int (num, 0x9910); + fail_unless (gst_byte_reader_dup_string (&reader, &s)); + fail_unless_equals_string (s, "f00!"); + fail_unless (gst_byte_reader_get_uint8 (&reader, &x)); + fail_unless_equals_int (x, 0xff); + g_free (s); +} + +GST_END_TEST; + static Suite * gst_byte_reader_suite (void) { @@ -568,6 +697,8 @@ gst_byte_reader_suite (void) tcase_add_test (tc_chain, test_get_float_be); tcase_add_test (tc_chain, test_position_tracking); tcase_add_test (tc_chain, test_scan); + tcase_add_test (tc_chain, test_string_funcs); + tcase_add_test (tc_chain, test_dup_string); return s; } |