diff options
Diffstat (limited to 'libsoup/soup-message-body.c')
-rw-r--r-- | libsoup/soup-message-body.c | 438 |
1 files changed, 63 insertions, 375 deletions
diff --git a/libsoup/soup-message-body.c b/libsoup/soup-message-body.c index eb6d5f5a..82c6b887 100644 --- a/libsoup/soup-message-body.c +++ b/libsoup/soup-message-body.c @@ -21,12 +21,6 @@ * * #SoupMessageBody represents the request or response body of a * #SoupMessage. - * - * In addition to #SoupMessageBody, libsoup also defines a "smaller" - * data buffer type, #SoupBuffer, which is primarily used as a - * component of #SoupMessageBody. In particular, when using chunked - * encoding to transmit or receive a message, each chunk is - * represented as a #SoupBuffer. **/ /** @@ -34,311 +28,12 @@ * @SOUP_MEMORY_STATIC: The memory is statically allocated and * constant; libsoup can use the passed-in buffer directly and not * need to worry about it being modified or freed. - * @SOUP_MEMORY_TAKE: The caller has allocated the memory for the - * #SoupBuffer's use; libsoup will assume ownership of it and free it - * (with g_free()) when it is done with it. - * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller; the - * #SoupBuffer will copy it into new memory, leaving the caller free + * @SOUP_MEMORY_TAKE: The caller has allocated the memory and libsoup + * will assume ownership of it and free it with g_free(). + * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller and + * libsoup will copy it into new memory leaving the caller free * to reuse the original memory. - * @SOUP_MEMORY_TEMPORARY: The passed-in data belongs to the caller, - * but will remain valid for the lifetime of the #SoupBuffer. The - * difference between this and @SOUP_MEMORY_STATIC is that if you copy - * a @SOUP_MEMORY_TEMPORARY buffer, it will make a copy of the memory - * as well, rather than reusing the original memory. - * - * Describes how #SoupBuffer should use the data passed in by the - * caller. - * - * See also soup_buffer_new_with_owner(), which allows to you create a - * buffer containing data which is owned by another object. - **/ - -/* Internal SoupMemoryUse values */ -enum { - SOUP_MEMORY_SUBBUFFER = SOUP_MEMORY_TEMPORARY + 1, - SOUP_MEMORY_OWNED -}; - -/** - * SoupBuffer: - * @data: (type gpointer): the data - * @length: length of @data - * - * A data buffer, generally used to represent a chunk of a - * #SoupMessageBody. - * - * @data is a #char because that's generally convenient; in some - * situations you may need to cast it to #guchar or another type. - **/ - -typedef struct { - SoupBuffer buffer; - SoupMemoryUse use; - guint refcount; - - gpointer owner; - GDestroyNotify owner_dnotify; -} SoupBufferPrivate; - -/** - * soup_buffer_new: - * @use: how @data is to be used by the buffer - * @data: (array length=length) (element-type guint8): data - * @length: length of @data - * - * Creates a new #SoupBuffer containing @length bytes from @data. - * - * Return value: the new #SoupBuffer. - **/ -SoupBuffer * -soup_buffer_new (SoupMemoryUse use, gconstpointer data, gsize length) -{ - SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate); - - if (use == SOUP_MEMORY_COPY) { - data = g_memdup (data, length); - use = SOUP_MEMORY_TAKE; - } - - priv->buffer.data = data; - priv->buffer.length = length; - priv->use = use; - priv->refcount = 1; - - if (use == SOUP_MEMORY_TAKE) { - priv->owner = (gpointer)data; - priv->owner_dnotify = g_free; - } - - return (SoupBuffer *)priv; -} - -/** - * soup_buffer_new_take: (rename-to soup_buffer_new) - * @data: (array length=length) (transfer full): data - * @length: length of @data - * - * Creates a new #SoupBuffer containing @length bytes from @data. - * - * This function is exactly equivalent to soup_buffer_new() with - * %SOUP_MEMORY_TAKE as first argument; it exists mainly for - * convenience and simplifying language bindings. - * - * Return value: the new #SoupBuffer. - * - * Since: 2.32 - **/ -SoupBuffer * -soup_buffer_new_take (guchar *data, gsize length) -{ - return soup_buffer_new (SOUP_MEMORY_TAKE, data, length); -} - -/** - * soup_buffer_new_subbuffer: - * @parent: the parent #SoupBuffer - * @offset: offset within @parent to start at - * @length: number of bytes to copy from @parent - * - * Creates a new #SoupBuffer containing @length bytes "copied" from - * @parent starting at @offset. (Normally this will not actually copy - * any data, but will instead simply reference the same data as - * @parent does.) - * - * Return value: the new #SoupBuffer. - **/ -SoupBuffer * -soup_buffer_new_subbuffer (SoupBuffer *parent, gsize offset, gsize length) -{ - SoupBufferPrivate *priv; - - /* Normally this is just a ref, but if @parent is TEMPORARY, - * it will do an actual copy. - */ - parent = soup_buffer_copy (parent); - - priv = g_slice_new0 (SoupBufferPrivate); - priv->buffer.data = parent->data + offset; - priv->buffer.length = length; - priv->use = SOUP_MEMORY_SUBBUFFER; - priv->owner = parent; - priv->owner_dnotify = (GDestroyNotify)soup_buffer_free; - priv->refcount = 1; - - return (SoupBuffer *)priv; -} - -/** - * soup_buffer_new_with_owner: - * @data: (array length=length) (element-type guint8): data - * @length: length of @data - * @owner: pointer to an object that owns @data - * @owner_dnotify: (allow-none): a function to free/unref @owner when - * the buffer is freed - * - * Creates a new #SoupBuffer containing @length bytes from @data. When - * the #SoupBuffer is freed, it will call @owner_dnotify, passing - * @owner to it. You must ensure that @data will remain valid until - * @owner_dnotify is called. - * - * For example, you could use this to create a buffer containing data - * returned from libxml without needing to do an extra copy: - * - * <informalexample><programlisting> - * xmlDocDumpMemory (doc, &xmlbody, &len); - * return soup_buffer_new_with_owner (xmlbody, len, xmlbody, - * (GDestroyNotify)xmlFree); - * </programlisting></informalexample> - * - * In this example, @data and @owner are the same, but in other cases - * they would be different (eg, @owner would be a object, and @data - * would be a pointer to one of the object's fields). - * - * Return value: the new #SoupBuffer. - **/ -SoupBuffer * -soup_buffer_new_with_owner (gconstpointer data, gsize length, - gpointer owner, GDestroyNotify owner_dnotify) -{ - SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate); - - priv->buffer.data = data; - priv->buffer.length = length; - priv->use = SOUP_MEMORY_OWNED; - priv->owner = owner; - priv->owner_dnotify = owner_dnotify; - priv->refcount = 1; - - return (SoupBuffer *)priv; -} - -/** - * soup_buffer_get_owner: - * @buffer: a #SoupBuffer created with soup_buffer_new_with_owner() - * - * Gets the "owner" object for a buffer created with - * soup_buffer_new_with_owner(). - * - * Return value: (transfer none): the owner pointer - **/ -gpointer -soup_buffer_get_owner (SoupBuffer *buffer) -{ - SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer; - - g_return_val_if_fail ((int)priv->use == (int)SOUP_MEMORY_OWNED, NULL); - return priv->owner; -} - -/** - * soup_buffer_get_data: - * @buffer: a #SoupBuffer - * @data: (out) (array length=length) (transfer none): the pointer - * to the buffer data is stored here - * @length: (out): the length of the buffer data is stored here - * - * This function exists for use by language bindings, because it's not - * currently possible to get the right effect by annotating the fields - * of #SoupBuffer. - * - * Since: 2.32 - */ -void -soup_buffer_get_data (SoupBuffer *buffer, - const guint8 **data, - gsize *length) -{ - *data = (const guint8 *)buffer->data; - *length = buffer->length; -} - -/** - * soup_buffer_copy: - * @buffer: a #SoupBuffer - * - * Makes a copy of @buffer. In reality, #SoupBuffer is a refcounted - * type, and calling soup_buffer_copy() will normally just increment - * the refcount on @buffer and return it. However, if @buffer was - * created with #SOUP_MEMORY_TEMPORARY memory, then soup_buffer_copy() - * will actually return a copy of it, so that the data in the copy - * will remain valid after the temporary buffer is freed. - * - * Return value: the new (or newly-reffed) buffer - **/ -SoupBuffer * -soup_buffer_copy (SoupBuffer *buffer) -{ - SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer; - - /* For non-TEMPORARY buffers, this is just a ref */ - if (priv->use != SOUP_MEMORY_TEMPORARY) { - g_atomic_int_inc (&priv->refcount); - return buffer; - } - - /* For TEMPORARY buffers, we need to do a real copy the first - * time, and then after that, we just keep returning the copy. - * We store the copy in priv->owner, which is technically - * backwards, but it saves us from having to keep an extra - * pointer in SoupBufferPrivate. - */ - - if (!priv->owner) { - priv->owner = soup_buffer_new (SOUP_MEMORY_COPY, - buffer->data, - buffer->length); - priv->owner_dnotify = (GDestroyNotify)soup_buffer_free; - } - return soup_buffer_copy (priv->owner); -} - -/** - * soup_buffer_free: - * @buffer: a #SoupBuffer - * - * Frees @buffer. (In reality, as described in the documentation for - * soup_buffer_copy(), this is actually an "unref" operation, and may - * or may not actually free @buffer.) **/ -void -soup_buffer_free (SoupBuffer *buffer) -{ - SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer; - - if (!g_atomic_int_dec_and_test (&priv->refcount)) - return; - - if (priv->owner_dnotify) - priv->owner_dnotify (priv->owner); - g_slice_free (SoupBufferPrivate, priv); -} - -/** - * soup_buffer_get_as_bytes: - * @buffer: a #SoupBuffer - * - * Creates a #GBytes pointing to the same memory as @buffer. The - * #GBytes will hold a reference on @buffer to ensure that it is not - * freed while the #GBytes is still valid. - * - * Returns: (transfer full): a new #GBytes which has the same content - * as the #SoupBuffer. - * - * Since: 2.40 - */ -GBytes * -soup_buffer_get_as_bytes (SoupBuffer *buffer) -{ - SoupBuffer *copy; - - copy = soup_buffer_copy (buffer); - return g_bytes_new_with_free_func (copy->data, copy->length, - (GDestroyNotify)soup_buffer_free, - copy); -} - -G_DEFINE_BOXED_TYPE (SoupBuffer, soup_buffer, soup_buffer_copy, soup_buffer_free) - /** * SoupMessageBody: @@ -362,7 +57,7 @@ G_DEFINE_BOXED_TYPE (SoupBuffer, soup_buffer, soup_buffer_copy, soup_buffer_free typedef struct { SoupMessageBody body; GSList *chunks, *last; - SoupBuffer *flattened; + GBytes *flattened; gboolean accumulate; goffset base_offset; int ref_count; @@ -451,7 +146,7 @@ soup_message_body_get_accumulate (SoupMessageBody *body) } static void -append_buffer (SoupMessageBody *body, SoupBuffer *buffer) +append_buffer (SoupMessageBody *body, GBytes *buffer) { SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body; @@ -461,12 +156,9 @@ append_buffer (SoupMessageBody *body, SoupBuffer *buffer) } else priv->chunks = priv->last = g_slist_append (NULL, buffer); - if (priv->flattened) { - soup_buffer_free (priv->flattened); - priv->flattened = NULL; - body->data = NULL; - } - body->length += buffer->length; + g_clear_pointer (&priv->flattened, g_bytes_unref); + body->data = NULL; + body->length += g_bytes_get_size (buffer); } /** @@ -482,8 +174,16 @@ void soup_message_body_append (SoupMessageBody *body, SoupMemoryUse use, gconstpointer data, gsize length) { - if (length > 0) - append_buffer (body, soup_buffer_new (use, data, length)); + GBytes *bytes; + if (length > 0) { + if (use == SOUP_MEMORY_TAKE) + bytes = g_bytes_new_take ((guchar*)data, length); + else if (use == SOUP_MEMORY_STATIC) + bytes = g_bytes_new_static (data, length); + else + bytes = g_bytes_new (data, length); + append_buffer (body, g_steal_pointer (&bytes)); + } else if (use == SOUP_MEMORY_TAKE) g_free ((gpointer)data); } @@ -510,20 +210,17 @@ soup_message_body_append_take (SoupMessageBody *body, } /** - * soup_message_body_append_buffer: + * soup_message_body_append_bytes: * @body: a #SoupMessageBody - * @buffer: a #SoupBuffer + * @buffer: a #GBytes * - * Appends the data from @buffer to @body. (#SoupMessageBody uses - * #SoupBuffers internally, so this is normally a constant-time - * operation that doesn't actually require copying the data in - * @buffer.) + * Appends the data from @buffer to @body. **/ void -soup_message_body_append_buffer (SoupMessageBody *body, SoupBuffer *buffer) +soup_message_body_append_bytes (SoupMessageBody *body, GBytes *buffer) { - g_return_if_fail (buffer->length > 0); - append_buffer (body, soup_buffer_copy (buffer)); + g_return_if_fail (g_bytes_get_size (buffer) > 0); + append_buffer (body, g_bytes_ref (buffer)); } /** @@ -537,15 +234,11 @@ soup_message_body_truncate (SoupMessageBody *body) { SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body; - g_slist_free_full (priv->chunks, (GDestroyNotify)soup_buffer_free); + g_slist_free_full (priv->chunks, (GDestroyNotify)g_bytes_unref); priv->chunks = priv->last = NULL; priv->base_offset = 0; - - if (priv->flattened) { - soup_buffer_free (priv->flattened); - priv->flattened = NULL; - body->data = NULL; - } + g_clear_pointer (&priv->flattened, g_bytes_unref); + body->data = NULL; body->length = 0; } @@ -559,7 +252,7 @@ soup_message_body_truncate (SoupMessageBody *body) void soup_message_body_complete (SoupMessageBody *body) { - append_buffer (body, soup_buffer_new (SOUP_MEMORY_STATIC, NULL, 0)); + append_buffer (body, g_bytes_new_static (NULL, 0)); } /** @@ -570,16 +263,13 @@ soup_message_body_complete (SoupMessageBody *body) * data in @body (plus an additional '\0' byte not counted by @body's * length field). * - * Return value: a #SoupBuffer containing the same data as @body. - * (You must free this buffer if you do not want it.) + * Return: (transfer full): a #GBytes containing the same data as @body. + * (You must g_bytes_unref() this if you do not want it.) **/ -SoupBuffer * +GBytes * soup_message_body_flatten (SoupMessageBody *body) { SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body; - char *buf, *ptr; - GSList *iter; - SoupBuffer *chunk; g_return_val_if_fail (priv->accumulate == TRUE, NULL); @@ -588,20 +278,22 @@ soup_message_body_flatten (SoupMessageBody *body) g_return_val_if_fail (body->length < G_MAXSIZE, NULL); #endif - buf = ptr = g_malloc (body->length + 1); - for (iter = priv->chunks; iter; iter = iter->next) { - chunk = iter->data; - memcpy (ptr, chunk->data, chunk->length); - ptr += chunk->length; + GByteArray *array = g_byte_array_sized_new (body->length + 1); + for (GSList *iter = priv->chunks; iter; iter = iter->next) { + GBytes *chunk = iter->data; + gsize chunk_size; + const guchar *chunk_data = g_bytes_get_data (chunk, &chunk_size); + g_byte_array_append (array, chunk_data, chunk_size); } - *ptr = '\0'; + // NUL terminate the array but don't reflect that in the length + g_byte_array_append (array, (guchar*)"\0", 1); + array->len -= 1; - priv->flattened = soup_buffer_new (SOUP_MEMORY_TAKE, - buf, body->length); - body->data = priv->flattened->data; + priv->flattened = g_byte_array_free_to_bytes (array); + body->data = g_bytes_get_data (priv->flattened, NULL); } - return soup_buffer_copy (priv->flattened); + return g_bytes_ref (priv->flattened); } /** @@ -609,7 +301,7 @@ soup_message_body_flatten (SoupMessageBody *body) * @body: a #SoupMessageBody * @offset: an offset * - * Gets a #SoupBuffer containing data from @body starting at @offset. + * Gets a #GBytes containing data from @body starting at @offset. * The size of the returned chunk is unspecified. You can iterate * through the entire body by first calling * soup_message_body_get_chunk() with an offset of 0, and then on each @@ -625,44 +317,40 @@ soup_message_body_flatten (SoupMessageBody *body) * @body may still potentially have more data, but that data is not * currently available). * - * Return value: (nullable): a #SoupBuffer, or %NULL. + * Return value: (nullable): a #GBytes, or %NULL. **/ -SoupBuffer * +GBytes * soup_message_body_get_chunk (SoupMessageBody *body, goffset offset) { SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body; GSList *iter; - SoupBuffer *chunk = NULL; + GBytes *chunk = NULL; offset -= priv->base_offset; for (iter = priv->chunks; iter; iter = iter->next) { chunk = iter->data; + gsize chunk_length = g_bytes_get_size (chunk); - if (offset < chunk->length || offset == 0) + if (offset < chunk_length || offset == 0) break; - offset -= chunk->length; + offset -= chunk_length; } if (!iter) return NULL; - if (offset == 0) - return soup_buffer_copy (chunk); - else { - return soup_buffer_new_subbuffer (chunk, offset, - chunk->length - offset); - } + return g_bytes_new_from_bytes (chunk, offset, g_bytes_get_size (chunk) - offset); } /** * soup_message_body_got_chunk: * @body: a #SoupMessageBody - * @chunk: a #SoupBuffer received from the network + * @chunk: a #GBytes received from the network * * Handles the #SoupMessageBody part of receiving a chunk of data from * the network. Normally this means appending @chunk to @body, exactly - * as with soup_message_body_append_buffer(), but if you have set + * as with soup_message_body_append_bytes(), but if you have set * @body's accumulate flag to %FALSE, then that will not happen. * * This is a low-level method which you should not normally need to @@ -671,20 +359,20 @@ soup_message_body_get_chunk (SoupMessageBody *body, goffset offset) * Since: 2.24 **/ void -soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk) +soup_message_body_got_chunk (SoupMessageBody *body, GBytes *chunk) { SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body; if (!priv->accumulate) return; - soup_message_body_append_buffer (body, chunk); + soup_message_body_append_bytes (body, chunk); } /** * soup_message_body_wrote_chunk: * @body: a #SoupMessageBody - * @chunk: a #SoupBuffer returned from soup_message_body_get_chunk() + * @chunk: a #GBytes returned from soup_message_body_get_chunk() * * Handles the #SoupMessageBody part of writing a chunk of data to the * network. Normally this is a no-op, but if you have set @body's @@ -698,24 +386,24 @@ soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk) * Since: 2.24 **/ void -soup_message_body_wrote_chunk (SoupMessageBody *body, SoupBuffer *chunk) +soup_message_body_wrote_chunk (SoupMessageBody *body, GBytes *chunk) { SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body; - SoupBuffer *chunk2; + GBytes *chunk2; if (priv->accumulate) return; chunk2 = priv->chunks->data; - g_return_if_fail (chunk->length == chunk2->length); - g_return_if_fail (chunk == chunk2 || ((SoupBufferPrivate *)chunk2)->use == SOUP_MEMORY_TEMPORARY); + g_return_if_fail (g_bytes_get_size (chunk) == g_bytes_get_size (chunk2)); + g_return_if_fail (chunk == chunk2); priv->chunks = g_slist_remove (priv->chunks, chunk2); if (!priv->chunks) priv->last = NULL; - priv->base_offset += chunk2->length; - soup_buffer_free (chunk2); + priv->base_offset += g_bytes_get_size (chunk2); + g_bytes_unref (chunk2); } static SoupMessageBody * |