mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-19 16:21:17 +00:00
bitwriter: Add a generic bit writer
GstBitWriter provides a bit writer that can write any number of bits into a memory buffer. It provides functions for writing any number of bits into 8, 16, 32 and 64 bit variables. https://bugzilla.gnome.org/show_bug.cgi?id=707543
This commit is contained in:
parent
4fb02fc85b
commit
fb4fc8fc09
8 changed files with 1006 additions and 0 deletions
|
@ -44,6 +44,7 @@
|
|||
|
||||
<xi:include href="xml/gstadapter.xml" />
|
||||
<xi:include href="xml/gstbitreader.xml" />
|
||||
<xi:include href="xml/gstbitwriter.xml" />
|
||||
<xi:include href="xml/gstbytereader.xml" />
|
||||
<xi:include href="xml/gstbytewriter.xml" />
|
||||
<xi:include href="xml/gstcollectpads.xml" />
|
||||
|
|
|
@ -516,6 +516,62 @@ gst_bit_reader_peek_bits_uint8_unchecked
|
|||
GST_BIT_READER
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gstbitwriter</FILE>
|
||||
<TITLE>GstBitWriter</TITLE>
|
||||
<INCLUDE>gst/base/gstbitwriter.h</INCLUDE>
|
||||
GstBitWriter
|
||||
|
||||
gst_bit_writer_new
|
||||
gst_bit_writer_new_with_size
|
||||
gst_bit_writer_new_with_data
|
||||
|
||||
gst_bit_writer_free
|
||||
gst_bit_writer_free_and_get_buffer
|
||||
gst_bit_writer_free_and_get_data
|
||||
|
||||
gst_bit_writer_init
|
||||
gst_bit_writer_init_with_size
|
||||
gst_bit_writer_init_with_data
|
||||
|
||||
gst_bit_writer_reset
|
||||
gst_bit_writer_reset_and_get_buffer
|
||||
gst_bit_writer_reset_and_get_data
|
||||
|
||||
gst_bit_writer_set_pos
|
||||
|
||||
gst_bit_writer_get_size
|
||||
gst_bit_writer_get_data
|
||||
gst_bit_writer_get_remaining
|
||||
|
||||
gst_bit_writer_get_size_unchecked
|
||||
gst_bit_writer_get_data_unchecked
|
||||
gst_bit_writer_set_pos_unchecked
|
||||
gst_bit_writer_get_remaining_unchecked
|
||||
|
||||
gst_bit_writer_put_bits_uint16
|
||||
gst_bit_writer_put_bits_uint32
|
||||
gst_bit_writer_put_bits_uint64
|
||||
gst_bit_writer_put_bits_uint8
|
||||
gst_bit_writer_put_bytes
|
||||
|
||||
gst_bit_writer_put_bits_uint16_unchecked
|
||||
gst_bit_writer_put_bits_uint32_unchecked
|
||||
gst_bit_writer_put_bits_uint64_unchecked
|
||||
gst_bit_writer_put_bits_uint8_unchecked
|
||||
gst_bit_writer_put_bytes_unchecked
|
||||
|
||||
gst_bit_writer_align_bytes
|
||||
gst_bit_writer_align_bytes_unchecked
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GST_BIT_WRITER_BIT_SIZE
|
||||
GST_BIT_WRITER_DATA
|
||||
|
||||
<SUBSECTION Private>
|
||||
GST_BIT_WRITER
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gstbytereader</FILE>
|
||||
<TITLE>GstByteReader</TITLE>
|
||||
|
|
|
@ -10,6 +10,7 @@ libgstbase_@GST_API_VERSION@_la_SOURCES = \
|
|||
gstbasesrc.c \
|
||||
gstbasetransform.c \
|
||||
gstbitreader.c \
|
||||
gstbitwriter.c \
|
||||
gstbytereader.c \
|
||||
gstbytewriter.c \
|
||||
gstcollectpads.c \
|
||||
|
@ -36,6 +37,7 @@ libgstbase_@GST_API_VERSION@include_HEADERS = \
|
|||
gstbasesrc.h \
|
||||
gstbasetransform.h \
|
||||
gstbitreader.h \
|
||||
gstbitwriter.h \
|
||||
gstbytereader.h \
|
||||
gstbytewriter.h \
|
||||
gstcollectpads.h \
|
||||
|
@ -49,6 +51,7 @@ noinst_HEADERS = \
|
|||
gstbytereader-docs.h \
|
||||
gstbytewriter-docs.h \
|
||||
gstbitreader-docs.h \
|
||||
gstbitwriter-docs.h \
|
||||
gstindex.h
|
||||
|
||||
EXTRA_DIST = gstindex.c gstmemindex.c
|
||||
|
|
92
libs/gst/base/gstbitwriter-docs.h
Normal file
92
libs/gst/base/gstbitwriter-docs.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* GStreamer bit writer dummy header for gtk-doc
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* This header is not installed, it just contains stuff for gtk-doc to parse,
|
||||
* in particular docs and some dummy function declarations for the static
|
||||
* inline functions we generate via macros in gstbitwriter.h.
|
||||
*/
|
||||
|
||||
#error "This header should never be included in code, it is only for gtk-doc"
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint8_unchecked:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint8 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter without checking whether
|
||||
* there is enough space
|
||||
*/
|
||||
void gst_bit_writer_put_bits_uint8_unchecked (GstBitWriter *bitwriter, guint8 value, guint nbits);
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint16_unchecked:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint16 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter without checking whether
|
||||
* there is enough space
|
||||
*/
|
||||
void gst_bit_writer_put_bits_uint16_unchecked (GstBitWriter *bitwriter, guint16 value, guint nbits);
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint32_unchecked:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint32 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter without checking whether
|
||||
* there is enough space
|
||||
*/
|
||||
void gst_bit_writer_put_bits_uint32_unchecked (GstBitWriter *bitwriter, guint32 value, guint nbits);
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint64_unchecked:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint64 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter without checking whether
|
||||
* there is enough space
|
||||
*/
|
||||
void gst_bit_writer_put_bits_uint64_unchecked (GstBitWriter *bitwriter, guint64 value, guint nbits);
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bytes_unchecked:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @data: pointer of data to write
|
||||
* @nbytes: number of bytes to write
|
||||
*
|
||||
* Write @nbytes bytes of @data to #GstBitWriter without checking whether
|
||||
* there is enough space
|
||||
*/
|
||||
void gst_bit_writer_put_bytes_unchecked (GstBitWriter *bitwriter, const guint8 *data, guint nbytes);
|
||||
|
||||
/**
|
||||
* gst_bit_writer_align_bytes_unchecked:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @trailing_bit: trailing bits of last byte, 0 or 1
|
||||
*
|
||||
* Write trailing bit to align last byte of @data without checking whether there
|
||||
* is enough space
|
||||
*/
|
||||
void gst_bit_writer_align_bytes_unchecked (GstBitWriter *bitwriter, guint8 trailing_bit);
|
444
libs/gst/base/gstbitwriter.c
Normal file
444
libs/gst/base/gstbitwriter.c
Normal file
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* gstbitwriter.c - bitstream writer
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
* Copyright (C) 2018 Igalia, S.L.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define GST_BIT_WRITER_DISABLE_INLINES
|
||||
#include "gstbitwriter.h"
|
||||
|
||||
/**
|
||||
* SECTION:gstbitwriter
|
||||
* @title: GstBitWriter
|
||||
* @short_description: Writes any number of bits into a memory buffer
|
||||
*
|
||||
* #GstBitWriter provides a bit writer that can write any number of
|
||||
* bits into a memory buffer. It provides functions for writing any
|
||||
* number of bits into 8, 16, 32 and 64 bit variables.
|
||||
*/
|
||||
|
||||
/**
|
||||
* gst_bit_writer_new:
|
||||
*
|
||||
* Creates a new, empty #GstBitWriter instance.
|
||||
*
|
||||
* Free-function: gst_bit_writer_free
|
||||
*
|
||||
* Returns: (transfer full): a new, empty #GstByteWriter instance
|
||||
**/
|
||||
GstBitWriter *
|
||||
gst_bit_writer_new (void)
|
||||
{
|
||||
GstBitWriter *ret = g_slice_new0 (GstBitWriter);
|
||||
|
||||
ret->owned = TRUE;
|
||||
ret->auto_grow = TRUE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_new_size:
|
||||
* @size: Initial size of data in bytes
|
||||
* @fixed: If %TRUE the data can't be reallocated
|
||||
*
|
||||
* Creates a #GstBitWriter instance with the given initial data size.
|
||||
*
|
||||
* Free-function: gst_bit_writer_free
|
||||
*
|
||||
* Returns: (transfer full): a new #GstBitWriter instance
|
||||
*/
|
||||
GstBitWriter *
|
||||
gst_bit_writer_new_with_size (guint size, gboolean fixed)
|
||||
{
|
||||
GstBitWriter *ret = g_slice_new0 (GstBitWriter);
|
||||
|
||||
gst_bit_writer_init_with_size (ret, size, fixed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_new_with_data:
|
||||
* @data: Memory area for writing
|
||||
* @size: Size of @data in bytes
|
||||
* @initialized: if %TRUE the complete data can be read from the beginning
|
||||
*
|
||||
* Creates a new #GstBitWriter instance with the given memory area. If
|
||||
* @initialized is %TRUE it is possible to read @size bits from the
|
||||
* #GstBitWriter from the beginnig.
|
||||
*
|
||||
* Free-function: gst_bit_writer_free
|
||||
*
|
||||
* Returns: (transfer full): a new #GstBitWriter instance
|
||||
*/
|
||||
GstBitWriter *
|
||||
gst_bit_writer_new_with_data (guint8 * data, guint size, gboolean initialized)
|
||||
{
|
||||
GstBitWriter *ret = g_slice_new0 (GstBitWriter);
|
||||
|
||||
gst_bit_writer_init_with_data (ret, data, size, initialized);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_init:
|
||||
* @bitwriter: #GstBitWriter instance
|
||||
*
|
||||
* Initializes @bitwriter to an empty instance.
|
||||
**/
|
||||
void
|
||||
gst_bit_writer_init (GstBitWriter * bitwriter)
|
||||
{
|
||||
g_return_if_fail (bitwriter != NULL);
|
||||
|
||||
memset (bitwriter, 0, sizeof (GstBitWriter));
|
||||
bitwriter->owned = TRUE;
|
||||
bitwriter->auto_grow = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_init_with_size:
|
||||
* @bitwriter: #GstBitWriter instance
|
||||
* @size: the size on bytes to allocate for data
|
||||
* @fixed: If %TRUE the data can't be reallocated
|
||||
*
|
||||
* Initializes a #GstBitWriter instance and allocates the given data
|
||||
* @size.
|
||||
*/
|
||||
void
|
||||
gst_bit_writer_init_with_size (GstBitWriter * bitwriter, guint size,
|
||||
gboolean fixed)
|
||||
{
|
||||
g_return_if_fail (bitwriter != NULL);
|
||||
|
||||
gst_bit_writer_init (bitwriter);
|
||||
|
||||
_gst_bit_writer_check_remaining (bitwriter, size << 3);
|
||||
|
||||
bitwriter->auto_grow = !fixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_init_with_data:
|
||||
* @bitwriter: #GstBitWriter instance
|
||||
* @data: (array length=size) (transfer none): Memory area for writing
|
||||
* @size: Size of @data in bytes
|
||||
* @initialized: If %TRUE the complete data can be read from the beginning
|
||||
*
|
||||
* Initializes @bitwriter with the given memory area @data. IF
|
||||
* @initialized is %TRUE it is possible to read @size bits from the
|
||||
* #GstBitWriter from the beginning.
|
||||
*/
|
||||
void
|
||||
gst_bit_writer_init_with_data (GstBitWriter * bitwriter, guint8 * data,
|
||||
guint size, gboolean initialized)
|
||||
{
|
||||
g_return_if_fail (bitwriter != NULL);
|
||||
|
||||
gst_bit_writer_init (bitwriter);
|
||||
|
||||
bitwriter->data = data;
|
||||
bitwriter->bit_capacity = size * 8;
|
||||
bitwriter->bit_size = (initialized) ? size << 3 : 0;
|
||||
bitwriter->auto_grow = FALSE;
|
||||
bitwriter->owned = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_reset:
|
||||
* @bitwriter: #GstBitWriter instance
|
||||
*
|
||||
* Resets @bitwriter and frees the data if it's owned by @bitwriter.
|
||||
*/
|
||||
void
|
||||
gst_bit_writer_reset (GstBitWriter * bitwriter)
|
||||
{
|
||||
g_return_if_fail (bitwriter != NULL);
|
||||
|
||||
if (bitwriter->owned)
|
||||
g_free (bitwriter->data);
|
||||
memset (bitwriter, 0, sizeof (GstBitWriter));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_reset_and_get_data:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
*
|
||||
* Resets @bitwriter and returns the current data.
|
||||
*
|
||||
* Free-function: g_free
|
||||
*
|
||||
* Returns: (array) (transfer full): the current data. g_free() after
|
||||
* usage.
|
||||
**/
|
||||
guint8 *
|
||||
gst_bit_writer_reset_and_get_data (GstBitWriter * bitwriter)
|
||||
{
|
||||
guint8 *data;
|
||||
|
||||
g_return_val_if_fail (bitwriter != NULL, NULL);
|
||||
|
||||
data = bitwriter->data;
|
||||
if (bitwriter->owned)
|
||||
data = g_memdup (data, bitwriter->bit_size >> 3);
|
||||
gst_bit_writer_reset (bitwriter);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_reset_and_get_buffer:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
*
|
||||
* Resets @bitwriter and returns the current data as #GstBuffer.
|
||||
*
|
||||
* Free-function: gst_buffer_unref
|
||||
*
|
||||
* Returns: (transfer full): a new allocated #GstBuffer wrapping the
|
||||
* current data. gst_buffer_unref() after usage.
|
||||
**/
|
||||
GstBuffer *
|
||||
gst_bit_writer_reset_and_get_buffer (GstBitWriter * bitwriter)
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
gpointer data;
|
||||
gsize size;
|
||||
|
||||
g_return_val_if_fail (bitwriter != NULL, NULL);
|
||||
|
||||
size = bitwriter->bit_size >> 3;
|
||||
data = gst_bit_writer_reset_and_get_data (bitwriter);
|
||||
|
||||
/* we cannot rely on buffers allocated externally, thus let's dup
|
||||
* the data */
|
||||
if (data && !bitwriter->owned)
|
||||
data = g_memdup (data, size);
|
||||
|
||||
buffer = gst_buffer_new ();
|
||||
if (data != NULL) {
|
||||
gst_buffer_append_memory (buffer,
|
||||
gst_memory_new_wrapped (0, data, size, 0, size, data, g_free));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_free:
|
||||
* @bitwriter: (in) (transfer full): #GstBitWriter instance
|
||||
*
|
||||
* Frees @bitwriter and the allocated data inside.
|
||||
*/
|
||||
void
|
||||
gst_bit_writer_free (GstBitWriter * bitwriter)
|
||||
{
|
||||
g_return_if_fail (bitwriter != NULL);
|
||||
|
||||
gst_bit_writer_reset (bitwriter);
|
||||
g_slice_free (GstBitWriter, bitwriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_free_and_get_data:
|
||||
* @bitwriter: (in) (transfer full): #GstBitWriter instance
|
||||
*
|
||||
* Frees @bitwriter without destroying the internal data, which is
|
||||
* returned.
|
||||
*
|
||||
* Free-function: g_free
|
||||
*
|
||||
* Returns: (array) (transfer full): the current data. g_free() after
|
||||
* usage.
|
||||
**/
|
||||
guint8 *
|
||||
gst_bit_writer_free_and_get_data (GstBitWriter * bitwriter)
|
||||
{
|
||||
guint8 *data;
|
||||
|
||||
g_return_val_if_fail (bitwriter != NULL, NULL);
|
||||
|
||||
data = gst_bit_writer_reset_and_get_data (bitwriter);
|
||||
g_slice_free (GstBitWriter, bitwriter);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_free_and_get_buffer:
|
||||
* @bitwriter: (in) (transfer full): #GstBitWriter instance
|
||||
*
|
||||
* Frees @bitwriter without destroying the internal data, which is
|
||||
* returned as #GstBuffer.
|
||||
*
|
||||
* Free-function: gst_buffer_unref
|
||||
*
|
||||
* Returns: (transfer full): a new allocated #GstBuffer wrapping the
|
||||
* data inside. gst_buffer_unref() after usage.
|
||||
**/
|
||||
GstBuffer *
|
||||
gst_bit_writer_free_and_get_buffer (GstBitWriter * bitwriter)
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
|
||||
g_return_val_if_fail (bitwriter != NULL, NULL);
|
||||
|
||||
buffer = gst_bit_writer_reset_and_get_buffer (bitwriter);
|
||||
g_slice_free (GstBitWriter, bitwriter);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_get_size:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
*
|
||||
* Get size of written @data
|
||||
*
|
||||
* Returns: size of bits written in @data
|
||||
*/
|
||||
guint
|
||||
gst_bit_writer_get_size (const GstBitWriter * bitwriter)
|
||||
{
|
||||
return _gst_bit_writer_get_size_inline (bitwriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_get_data:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
*
|
||||
* Get written data pointer
|
||||
*
|
||||
* Returns: data pointer
|
||||
*/
|
||||
guint8 *
|
||||
gst_bit_writer_get_data (const GstBitWriter * bitwriter)
|
||||
{
|
||||
return _gst_bit_writer_get_data_inline (bitwriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_get_pos:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @pos: The new position in bits
|
||||
*
|
||||
* Set the new postion of data end which should be the new size of @data.
|
||||
*
|
||||
* Returns: %TRUE if successful, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_bit_writer_set_pos (GstBitWriter * bitwriter, guint pos)
|
||||
{
|
||||
return _gst_bit_writer_set_pos_inline (bitwriter, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint8:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint8 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter.
|
||||
*
|
||||
* Returns: %TRUE if successful, %FALSE otherwise.
|
||||
*/
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint16:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint16 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter.
|
||||
*
|
||||
* Returns: %TRUE if successful, %FALSE otherwise.
|
||||
*/
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint32:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint32 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter.
|
||||
*
|
||||
* Returns: %TRUE if successful, %FALSE otherwise.
|
||||
*/
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bits_uint64:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @value: value of #guint64 to write
|
||||
* @nbits: number of bits to write
|
||||
*
|
||||
* Write @nbits bits of @value to #GstBitWriter.
|
||||
*
|
||||
* Returns: %TRUE if successful, %FALSE otherwise.
|
||||
*/
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#define GST_BIT_WRITER_WRITE_BITS(bits) \
|
||||
gboolean \
|
||||
gst_bit_writer_put_bits_uint##bits (GstBitWriter *bitwriter, guint##bits value, guint nbits) \
|
||||
{ \
|
||||
return _gst_bit_writer_put_bits_uint##bits##_inline (bitwriter, value, nbits); \
|
||||
}
|
||||
|
||||
GST_BIT_WRITER_WRITE_BITS (8)
|
||||
GST_BIT_WRITER_WRITE_BITS (16)
|
||||
GST_BIT_WRITER_WRITE_BITS (32)
|
||||
GST_BIT_WRITER_WRITE_BITS (64)
|
||||
#undef GST_BIT_WRITER_WRITE_BITS
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/**
|
||||
* gst_bit_writer_put_bytes:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @data: pointer of data to write
|
||||
* @nbytes: number of bytes to write
|
||||
*
|
||||
* Write @nbytes bytes of @data to #GstBitWriter.
|
||||
*
|
||||
* Returns: %TRUE if successful, %FALSE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
gst_bit_writer_put_bytes (GstBitWriter * bitwriter, const guint8 * data,
|
||||
guint nbytes)
|
||||
{
|
||||
return _gst_bit_writer_put_bytes_inline (bitwriter, data, nbytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_bit_writer_align_bytes:
|
||||
* @bitwriter: a #GstBitWriter instance
|
||||
* @trailing_bit: trailing bits of last byte, 0 or 1
|
||||
*
|
||||
* Write trailing bit to align last byte of @data. @trailing_bit can
|
||||
* only be 1 or 0.
|
||||
*
|
||||
* Returns: %TRUE if successful, %FALSE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
gst_bit_writer_align_bytes (GstBitWriter * bitwriter, guint8 trailing_bit)
|
||||
{
|
||||
return _gst_bit_writer_align_bytes_inline (bitwriter, trailing_bit);
|
||||
}
|
386
libs/gst/base/gstbitwriter.h
Normal file
386
libs/gst/base/gstbitwriter.h
Normal file
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
* gstbitwriter.h - bitstream writer
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
* Copyright (C) 2018 Igalia, S. L.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef GST_BIT_WRITER_H
|
||||
#define GST_BIT_WRITER_H
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/base-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_BIT_WRITER_DATA(writer) ((writer)->data)
|
||||
#define GST_BIT_WRITER_BIT_SIZE(writer) ((writer)->bit_size)
|
||||
#define GST_BIT_WRITER(writer) ((GstBitWriter *) (writer))
|
||||
|
||||
typedef struct _GstBitWriter GstBitWriter;
|
||||
|
||||
/**
|
||||
* GstBitWriter:
|
||||
* @data: Allocated @data for bit writer to write
|
||||
* @bit_size: Size of written @data in bits
|
||||
*
|
||||
* Private:
|
||||
* @bit_capacity: Capacity of the allocated @data
|
||||
* @auto_grow: @data space can auto grow
|
||||
* @destroy_data: The #GDestroyNotify function called with #data when the memory
|
||||
* is freed
|
||||
*
|
||||
* A bit writer instance.
|
||||
*/
|
||||
struct _GstBitWriter
|
||||
{
|
||||
guint8 *data;
|
||||
guint bit_size;
|
||||
|
||||
/*< private >*/
|
||||
guint bit_capacity;
|
||||
gboolean auto_grow;
|
||||
gboolean owned;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_BASE_API
|
||||
GstBitWriter * gst_bit_writer_new (void) G_GNUC_MALLOC;
|
||||
|
||||
GST_BASE_API
|
||||
GstBitWriter * gst_bit_writer_new_with_size (guint32 size, gboolean fixed) G_GNUC_MALLOC;
|
||||
|
||||
GST_BASE_API
|
||||
GstBitWriter * gst_bit_writer_new_with_data (guint8 *data, guint size,
|
||||
gboolean initialized) G_GNUC_MALLOC;
|
||||
|
||||
GST_BASE_API
|
||||
void gst_bit_writer_free (GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
guint8 * gst_bit_writer_free_and_get_data (GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
GstBuffer * gst_bit_writer_free_and_get_buffer (GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
void gst_bit_writer_init (GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
void gst_bit_writer_init_with_size (GstBitWriter *bitwriter,
|
||||
guint32 size, gboolean fixed);
|
||||
|
||||
GST_BASE_API
|
||||
void gst_bit_writer_init_with_data (GstBitWriter *bitwriter, guint8 *data,
|
||||
guint size, gboolean initialized);
|
||||
|
||||
GST_BASE_API
|
||||
void gst_bit_writer_reset (GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
guint8 * gst_bit_writer_reset_and_get_data (GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
GstBuffer * gst_bit_writer_reset_and_get_buffer (GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
guint gst_bit_writer_get_size (const GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
guint8 * gst_bit_writer_get_data (const GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_bit_writer_set_pos (GstBitWriter *bitwriter, guint pos);
|
||||
|
||||
GST_BASE_API
|
||||
guint gst_bit_writer_get_remaining (const GstBitWriter *bitwriter);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_bit_writer_put_bits_uint8 (GstBitWriter *bitwriter, guint8 value,
|
||||
guint nbits);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_bit_writer_put_bits_uint16 (GstBitWriter *bitwriter, guint16 value,
|
||||
guint nbits);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_bit_writer_put_bits_uint32 (GstBitWriter *bitwriter, guint32 value,
|
||||
guint nbits);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_bit_writer_put_bits_uint64 (GstBitWriter *bitwriter, guint64 value,
|
||||
guint nbits);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_bit_writer_put_bytes (GstBitWriter *bitwriter, const guint8 *data,
|
||||
guint nbytes);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_bit_writer_align_bytes (GstBitWriter *bitwriter, guint8 trailing_bit);
|
||||
|
||||
static const guint8 _gst_bit_writer_bit_filling_mask[9] = {
|
||||
0x00, 0x01, 0x03, 0x07,
|
||||
0x0F, 0x1F, 0x3F, 0x7F,
|
||||
0xFF
|
||||
};
|
||||
|
||||
/* Aligned to 256 bytes */
|
||||
#define __GST_BITS_WRITER_ALIGNMENT_MASK 2047
|
||||
#define __GST_BITS_WRITER_ALIGNED(bitsize) \
|
||||
(((bitsize) + __GST_BITS_WRITER_ALIGNMENT_MASK)&(~__GST_BITS_WRITER_ALIGNMENT_MASK))
|
||||
|
||||
static inline gboolean
|
||||
_gst_bit_writer_check_remaining (GstBitWriter * bitwriter, guint32 bits)
|
||||
{
|
||||
guint32 new_bit_size = bits + bitwriter->bit_size;
|
||||
guint32 clear_pos;
|
||||
|
||||
g_assert (bitwriter->bit_size <= bitwriter->bit_capacity);
|
||||
if (new_bit_size <= bitwriter->bit_capacity)
|
||||
return TRUE;
|
||||
|
||||
if (!bitwriter->auto_grow)
|
||||
return FALSE;
|
||||
|
||||
/* auto grow space */
|
||||
new_bit_size = __GST_BITS_WRITER_ALIGNED (new_bit_size);
|
||||
g_assert (new_bit_size
|
||||
&& ((new_bit_size & __GST_BITS_WRITER_ALIGNMENT_MASK) == 0));
|
||||
clear_pos = ((bitwriter->bit_size + 7) >> 3);
|
||||
bitwriter->data = g_realloc (bitwriter->data, (new_bit_size >> 3));
|
||||
memset (bitwriter->data + clear_pos, 0, (new_bit_size >> 3) - clear_pos);
|
||||
bitwriter->bit_capacity = new_bit_size;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef __GST_BITS_WRITER_ALIGNMENT_MASK
|
||||
#undef __GST_BITS_WRITER_ALIGNED
|
||||
|
||||
#define __GST_BIT_WRITER_WRITE_BITS_UNCHECKED(bits) \
|
||||
static inline void \
|
||||
gst_bit_writer_put_bits_uint##bits##_unchecked( \
|
||||
GstBitWriter *bitwriter, \
|
||||
guint##bits value, \
|
||||
guint nbits \
|
||||
) \
|
||||
{ \
|
||||
guint byte_pos, bit_offset; \
|
||||
guint8 *cur_byte; \
|
||||
guint fill_bits; \
|
||||
\
|
||||
byte_pos = (bitwriter->bit_size >> 3); \
|
||||
bit_offset = (bitwriter->bit_size & 0x07); \
|
||||
cur_byte = bitwriter->data + byte_pos; \
|
||||
g_assert (nbits <= bits); \
|
||||
g_assert( bit_offset < 8 && \
|
||||
bitwriter->bit_size <= bitwriter->bit_capacity); \
|
||||
\
|
||||
while (nbits) { \
|
||||
fill_bits = ((8 - bit_offset) < nbits ? (8 - bit_offset) : nbits); \
|
||||
nbits -= fill_bits; \
|
||||
bitwriter->bit_size += fill_bits; \
|
||||
\
|
||||
*cur_byte |= (((value >> nbits) & _gst_bit_writer_bit_filling_mask[fill_bits]) \
|
||||
<< (8 - bit_offset - fill_bits)); \
|
||||
++cur_byte; \
|
||||
bit_offset = 0; \
|
||||
} \
|
||||
g_assert(cur_byte <= \
|
||||
(bitwriter->data + (bitwriter->bit_capacity >> 3))); \
|
||||
}
|
||||
|
||||
__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (8)
|
||||
__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (16)
|
||||
__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (32)
|
||||
__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (64)
|
||||
#undef __GST_BIT_WRITER_WRITE_BITS_UNCHECKED
|
||||
|
||||
static inline guint
|
||||
gst_bit_writer_get_size_unchecked (const GstBitWriter * bitwriter)
|
||||
{
|
||||
return GST_BIT_WRITER_BIT_SIZE (bitwriter);
|
||||
}
|
||||
|
||||
static inline guint8 *
|
||||
gst_bit_writer_get_data_unchecked (const GstBitWriter * bitwriter)
|
||||
{
|
||||
return GST_BIT_WRITER_DATA (bitwriter);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gst_bit_writer_set_pos_unchecked (GstBitWriter * bitwriter, guint pos)
|
||||
{
|
||||
GST_BIT_WRITER_BIT_SIZE (bitwriter) = pos;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline guint
|
||||
gst_bit_writer_get_remaining_unchecked (const GstBitWriter * bitwriter)
|
||||
{
|
||||
return bitwriter->bit_capacity - bitwriter->bit_size;
|
||||
}
|
||||
|
||||
static inline void
|
||||
gst_bit_writer_put_bytes_unchecked (GstBitWriter * bitwriter,
|
||||
const guint8 * data, guint nbytes)
|
||||
{
|
||||
if ((bitwriter->bit_size & 0x07) == 0) {
|
||||
memcpy (&bitwriter->data[bitwriter->bit_size >> 3], data, nbytes);
|
||||
bitwriter->bit_size += (nbytes << 3);
|
||||
} else {
|
||||
g_assert (0);
|
||||
while (nbytes) {
|
||||
gst_bit_writer_put_bits_uint8_unchecked (bitwriter, *data, 8);
|
||||
--nbytes;
|
||||
++data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gst_bit_writer_align_bytes_unchecked (GstBitWriter * bitwriter,
|
||||
guint8 trailing_bit)
|
||||
{
|
||||
guint32 bit_offset, bit_left;
|
||||
guint8 value = 0;
|
||||
|
||||
bit_offset = (bitwriter->bit_size & 0x07);
|
||||
if (!bit_offset)
|
||||
return;
|
||||
|
||||
bit_left = 8 - bit_offset;
|
||||
if (trailing_bit)
|
||||
value = _gst_bit_writer_bit_filling_mask[bit_left];
|
||||
return gst_bit_writer_put_bits_uint8_unchecked (bitwriter, value, bit_left);
|
||||
}
|
||||
|
||||
#define __GST_BIT_WRITER_WRITE_BITS_INLINE(bits) \
|
||||
static inline gboolean \
|
||||
_gst_bit_writer_put_bits_uint##bits##_inline( \
|
||||
GstBitWriter *bitwriter, \
|
||||
guint##bits value, \
|
||||
guint nbits \
|
||||
) \
|
||||
{ \
|
||||
g_return_val_if_fail(bitwriter != NULL, FALSE); \
|
||||
g_return_val_if_fail(nbits != 0, FALSE); \
|
||||
g_return_val_if_fail(nbits <= bits, FALSE); \
|
||||
\
|
||||
if (!_gst_bit_writer_check_remaining(bitwriter, nbits)) \
|
||||
return FALSE; \
|
||||
gst_bit_writer_put_bits_uint##bits##_unchecked(bitwriter, value, nbits); \
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
__GST_BIT_WRITER_WRITE_BITS_INLINE (8)
|
||||
__GST_BIT_WRITER_WRITE_BITS_INLINE (16)
|
||||
__GST_BIT_WRITER_WRITE_BITS_INLINE (32)
|
||||
__GST_BIT_WRITER_WRITE_BITS_INLINE (64)
|
||||
#undef __GST_BIT_WRITER_WRITE_BITS_INLINE
|
||||
|
||||
static inline guint
|
||||
_gst_bit_writer_get_size_inline (const GstBitWriter * bitwriter)
|
||||
{
|
||||
g_return_val_if_fail (bitwriter != NULL, 0);
|
||||
|
||||
return gst_bit_writer_get_size_unchecked (bitwriter);
|
||||
}
|
||||
|
||||
static inline guint8 *
|
||||
_gst_bit_writer_get_data_inline (const GstBitWriter * bitwriter)
|
||||
{
|
||||
g_return_val_if_fail (bitwriter != NULL, NULL);
|
||||
|
||||
return gst_bit_writer_get_data_unchecked (bitwriter);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
_gst_bit_writer_set_pos_inline (GstBitWriter * bitwriter, guint pos)
|
||||
{
|
||||
g_return_val_if_fail (bitwriter != NULL, FALSE);
|
||||
g_return_val_if_fail (pos <= bitwriter->bit_capacity, FALSE);
|
||||
|
||||
return gst_bit_writer_set_pos_unchecked (bitwriter, pos);
|
||||
}
|
||||
|
||||
static inline guint
|
||||
_gst_bit_writer_get_remaining_inline (const GstBitWriter * bitwriter)
|
||||
{
|
||||
g_return_val_if_fail (bitwriter != NULL, 0);
|
||||
g_return_val_if_fail (bitwriter->bit_size < bitwriter->bit_capacity, 0);
|
||||
|
||||
return gst_bit_writer_get_remaining_unchecked (bitwriter);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
_gst_bit_writer_put_bytes_inline (GstBitWriter * bitwriter,
|
||||
const guint8 * data, guint nbytes)
|
||||
{
|
||||
g_return_val_if_fail (bitwriter != NULL, FALSE);
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
g_return_val_if_fail (nbytes, FALSE);
|
||||
|
||||
if (!_gst_bit_writer_check_remaining (bitwriter, nbytes * 8))
|
||||
return FALSE;
|
||||
|
||||
gst_bit_writer_put_bytes_unchecked (bitwriter, data, nbytes);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
_gst_bit_writer_align_bytes_inline (GstBitWriter * bitwriter,
|
||||
guint8 trailing_bit)
|
||||
{
|
||||
g_return_val_if_fail (bitwriter != NULL, FALSE);
|
||||
g_return_val_if_fail ((trailing_bit == 0 || trailing_bit == 1), FALSE);
|
||||
g_return_val_if_fail (((bitwriter->bit_size + 7) & (~7)) <=
|
||||
bitwriter->bit_capacity, FALSE);
|
||||
|
||||
gst_bit_writer_align_bytes_unchecked (bitwriter, trailing_bit);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifndef GST_BIT_WRITER_DISABLE_INLINES
|
||||
#define gst_bit_writer_get_size(bitwriter) \
|
||||
_gst_bit_writer_get_size_inline(bitwriter)
|
||||
#define gst_bit_writer_get_data(bitwriter) \
|
||||
_gst_bit_writer_get_data_inline(bitwriter)
|
||||
#define gst_bit_writer_set_pos(bitwriter, pos) \
|
||||
G_LIKELY (_gst_bit_writer_set_pos_inline (bitwriter, pos))
|
||||
#define gst_bit_writer_get_remaining(bitwriter) \
|
||||
_gst_bit_writer_get_remaining_inline(bitwriter)
|
||||
|
||||
#define gst_bit_writer_put_bits_uint8(bitwriter, value, nbits) \
|
||||
G_LIKELY (_gst_bit_writer_put_bits_uint8_inline (bitwriter, value, nbits))
|
||||
#define gst_bit_writer_put_bits_uint16(bitwriter, value, nbits) \
|
||||
G_LIKELY (_gst_bit_writer_put_bits_uint16_inline (bitwriter, value, nbits))
|
||||
#define gst_bit_writer_put_bits_uint32(bitwriter, value, nbits) \
|
||||
G_LIKELY (_gst_bit_writer_put_bits_uint32_inline (bitwriter, value, nbits))
|
||||
#define gst_bit_writer_put_bits_uint64(bitwriter, value, nbits) \
|
||||
G_LIKELY (_gst_bit_writer_put_bits_uint64_inline (bitwriter, value, nbits))
|
||||
|
||||
#define gst_bit_writer_put_bytes(bitwriter, data, nbytes) \
|
||||
G_LIKELY (_gst_bit_writer_put_bytes_inline (bitwriter, data, nbytes))
|
||||
|
||||
#define gst_bit_writer_align_bytes(bitwriter, trailing_bit) \
|
||||
G_LIKELY (_gst_bit_writer_align_bytes_inline(bitwriter, trailing_bit))
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* GST_BIT_WRITER_H */
|
|
@ -6,6 +6,7 @@ gst_base_sources = [
|
|||
'gstbasesrc.c',
|
||||
'gstbasetransform.c',
|
||||
'gstbitreader.c',
|
||||
'gstbitwriter.c',
|
||||
'gstbytereader.c',
|
||||
'gstbytewriter.c',
|
||||
'gstcollectpads.c',
|
||||
|
@ -26,6 +27,7 @@ gst_base_headers = [
|
|||
'gstbasesrc.h',
|
||||
'gstbasetransform.h',
|
||||
'gstbitreader.h',
|
||||
'gstbitwriter.h',
|
||||
'gstbytereader.h',
|
||||
'gstbytewriter.h',
|
||||
'gstcollectpads.h',
|
||||
|
@ -79,6 +81,7 @@ install_headers('base.h',
|
|||
'gstbasesrc.h',
|
||||
'gstbasetransform.h',
|
||||
'gstbitreader.h',
|
||||
'gstbitwriter.h',
|
||||
'gstbytereader.h',
|
||||
'gstbytewriter.h',
|
||||
'gstcollectpads.h',
|
||||
|
|
|
@ -149,6 +149,27 @@ EXPORTS
|
|||
gst_bit_reader_set_pos
|
||||
gst_bit_reader_skip
|
||||
gst_bit_reader_skip_to_byte
|
||||
gst_bit_writer_align_bytes
|
||||
gst_bit_writer_free
|
||||
gst_bit_writer_free_and_get_buffer
|
||||
gst_bit_writer_free_and_get_data
|
||||
gst_bit_writer_get_data
|
||||
gst_bit_writer_get_size
|
||||
gst_bit_writer_init
|
||||
gst_bit_writer_init_with_data
|
||||
gst_bit_writer_init_with_size
|
||||
gst_bit_writer_new
|
||||
gst_bit_writer_new_with_data
|
||||
gst_bit_writer_new_with_size
|
||||
gst_bit_writer_put_bits_uint16
|
||||
gst_bit_writer_put_bits_uint32
|
||||
gst_bit_writer_put_bits_uint64
|
||||
gst_bit_writer_put_bits_uint8
|
||||
gst_bit_writer_put_bytes
|
||||
gst_bit_writer_reset
|
||||
gst_bit_writer_reset_and_get_buffer
|
||||
gst_bit_writer_reset_and_get_data
|
||||
gst_bit_writer_set_pos
|
||||
gst_byte_reader_dup_data
|
||||
gst_byte_reader_dup_string_utf16
|
||||
gst_byte_reader_dup_string_utf32
|
||||
|
|
Loading…
Reference in a new issue