<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Dec 12, 2014 at 2:43 PM, Carl Worth <span dir="ltr"><<a href="mailto:cworth@cworth.org" target="_blank">cworth@cworth.org</a>></span> wrote:<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">This new interface allows for writing a series of objects to a chunk<br>
of memory (a "blob").. The allocated memory is maintained within the<br>
blob itself, (and re-allocated by doubling when necessary).<br>
<br>
There are also functions for reading objects from a blob as well. If<br>
code attempts to read beyond the available memory, the read functions<br>
return 0 values (or its moral equivalent) without reading past the<br>
allocated memory. Once the caller is done with the reads, it can check<br>
blob->overrun to ensure whether any invalid values were previously<br>
returned due to attempts to read too far.<br>
---<br>
<br>
</span>Jason,<br>
<br>
This second revision of my patch is in response to your review.<br>
<br>
I went to move the align_blob_reader function down by all the other<br>
blob_reader functions, but I decided not to. You had a concern about the<br>
alignment code, and it it's wrong then both align_blob() and<br>
align_blob_reader() will need to be fixed. So it seems useful to me to keep<br>
them close to each other.<br>
<br>
-Carl<br>
<br>
 src/glsl/Makefile.sources |   3 +-<br>
 src/glsl/blob.c           | 295 ++++++++++++++++++++++++++++++++++++++++++++++<br>
 src/glsl/blob.h           | 245 ++++++++++++++++++++++++++++++++++++++<br>
 3 files changed, 542 insertions(+), 1 deletion(-)<br>
<span class=""> create mode 100644 src/glsl/blob.c<br>
 create mode 100644 src/glsl/blob.h<br>
<br>
diff --git a/src/glsl/Makefile.sources b/src/glsl/Makefile.sources<br>
index 676fa0d..875e4bf 100644<br>
--- a/src/glsl/Makefile.sources<br>
+++ b/src/glsl/Makefile.sources<br>
@@ -105,7 +105,8 @@ LIBGLSL_FILES = \<br>
        $(GLSL_SRCDIR)/opt_swizzle_swizzle.cpp \<br>
        $(GLSL_SRCDIR)/opt_tree_grafting.cpp \<br>
        $(GLSL_SRCDIR)/opt_vectorize.cpp \<br>
-       $(GLSL_SRCDIR)/s_expression.cpp<br>
+       $(GLSL_SRCDIR)/s_expression.cpp \<br>
+       $(GLSL_SRCDIR)/blob.c<br>
<br>
 # glsl_compiler<br>
<br>
diff --git a/src/glsl/blob.c b/src/glsl/blob.c<br>
new file mode 100644<br>
</span>index 0000000..75d3cda<br>
--- /dev/null<br>
+++ b/src/glsl/blob.c<br>
@@ -0,0 +1,295 @@<br>
<span class="">+/*<br>
+ * Copyright © 2014 Intel Corporation<br>
+ *<br>
</span><span class="">+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
</span><div><div class="h5">+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS<br>
+ * IN THE SOFTWARE.<br>
+ */<br>
+<br>
+#include <string.h><br>
+<br>
+#include "main/macros.h"<br>
+#include "util/ralloc.h"<br>
+#include "blob.h"<br>
+<br>
+#define BLOB_INITIAL_SIZE 4096<br>
+<br>
+/* Ensure that \blob will be able to fit an additional object of size<br>
+ * \additional.  The growing (if any) will occur by doubling the existing<br>
+ * allocation.<br>
+ */<br>
+static bool<br>
+grow_to_fit (struct blob *blob, size_t additional)<br>
+{<br>
+   size_t to_allocate;<br>
+   uint8_t *new_data;<br>
+<br>
+   if (blob->size + additional <= blob->allocated)<br>
+      return true;<br>
+<br>
</div></div><span class="">+   if (blob->allocated == 0)<br>
+      to_allocate = BLOB_INITIAL_SIZE;<br>
+   else<br>
</span>+      to_allocate = MAX2(blob->allocated * 2, additional);<br></blockquote><div><br></div><div>Should be MAX2(blob->allocated * 2, blob->size + additional)<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<span class="">+<br>
+   new_data = reralloc_size(blob, blob->data, to_allocate);<br>
+   if (new_data == NULL)<br>
+      return false;<br>
+<br>
+   blob->data = new_data;<br>
+   blob->allocated = to_allocate;<br>
+<br>
+   return true;<br>
+}<br>
+<br>
+/* Align the blob->size so that reading or writing a value at (blob->data +<br>
+ * blob->size) will result in an access aligned to a granularity of \alignment<br>
+ * bytes.<br>
+ *<br>
+ * \return True unless allocation fails<br>
+ */<br>
</span><span class="">+static bool<br>
+align_blob (struct blob *blob, size_t alignment)<br>
+{<br>
+   size_t new_size;<br>
+<br>
+   new_size = ALIGN(blob->size, alignment);<br>
</span><span class="">+<br>
+   if (! grow_to_fit (blob, new_size - blob->size))<br>
+      return false;<br>
+<br>
+   blob->size = new_size;<br>
+<br>
+   return true;<br>
+}<br>
+<br>
</span><span class="">+static void<br>
+align_blob_reader (struct blob_reader *blob, size_t alignment)<br>
+{<br>
+   blob->current = blob->data + ALIGN(blob->current - blob->data, alignment);<br>
</span><div><div class="h5">+}<br>
+<br>
+struct blob *<br>
+blob_create (void *mem_ctx)<br>
+{<br>
+   struct blob *blob;<br>
+<br>
+   blob = ralloc(mem_ctx, struct blob);<br>
+   if (blob == NULL)<br>
+      return NULL;<br>
+<br>
+   blob->data = NULL;<br>
+   blob->allocated = 0;<br>
+   blob->size = 0;<br>
+<br>
+   return blob;<br>
+}<br>
+<br>
+bool<br>
+blob_write_bytes (struct blob *blob, const void *bytes, size_t to_write)<br>
+{<br>
+   if (! grow_to_fit (blob, to_write))<br>
+       return false;<br>
+<br>
+   memcpy (blob->data + blob->size, bytes, to_write);<br>
+   blob->size += to_write;<br>
+<br>
+   return true;<br>
+}<br>
+<br>
+uint8_t *<br>
+blob_reserve_bytes (struct blob *blob, size_t to_write)<br>
+{<br>
+   uint8_t *ret;<br>
+<br>
+   if (! grow_to_fit (blob, to_write))<br>
+      return NULL;<br>
+<br>
+   ret = blob->data + blob->size;<br>
+   blob->size += to_write;<br>
+<br>
+   return ret;<br>
+}<br>
+<br>
+bool<br>
+blob_write_uint32 (struct blob *blob, uint32_t value)<br>
+{<br>
+   align_blob(blob, sizeof(value));<br>
+<br>
+   return blob_write_bytes(blob, &value, sizeof(value));<br>
+}<br>
+<br>
+bool<br>
+blob_write_uint64 (struct blob *blob, uint64_t value)<br>
+{<br>
+   align_blob(blob, sizeof(value));<br>
+<br>
+   return blob_write_bytes(blob, &value, sizeof(value));<br>
+}<br>
+<br>
+bool<br>
+blob_write_intptr (struct blob *blob, intptr_t value)<br>
+{<br>
+   align_blob(blob, sizeof(value));<br>
+<br>
+   return blob_write_bytes(blob, &value, sizeof(value));<br>
+}<br>
+<br>
+bool<br>
+blob_write_string (struct blob *blob, const char *str)<br>
+{<br>
+   return blob_write_bytes(blob, str, strlen(str) + 1);<br>
+}<br>
+<br>
</div></div><span class="">+void<br>
+blob_reader_init (struct blob_reader *blob, uint8_t *data, size_t size)<br>
</span><span class="">+{<br>
+   blob->data = data;<br>
+   blob->end = data + size;<br>
+   blob->current = data;<br>
+   blob->overrun = false;<br>
+}<br>
+<br>
+/* Check that an object of size \size can be read from this blob.<br>
+ *<br>
+ * If not, set blob->overrun to indicate that we attempted to read too far.<br>
+ */<br>
+static bool<br>
</span>+ensure_can_read (struct blob_reader *blob, size_t size)<br>
+{<br>
+   if (blob->current < blob->end && blob->end - blob->current >= size)<br>
<span class="">+      return true;<br>
+<br>
+   blob->overrun = true;<br>
+<br>
+   return false;<br>
+}<br>
+<br>
</span><span class="">+void *<br>
+blob_read_bytes (struct blob_reader *blob, size_t size)<br>
+{<br>
+   void *ret;<br>
+<br>
</span>+   if (! ensure_can_read (blob, size))<br>
+      return NULL;<br>
+<br>
+   ret = blob->current;<br>
<span class="">+<br>
+   blob->current += size;<br>
+<br>
+   return ret;<br>
+}<br>
+<br>
+void<br>
+blob_copy_bytes (struct blob_reader *blob, uint8_t *dest, size_t size)<br>
+{<br>
+   uint8_t *bytes;<br>
+<br>
+   bytes = blob_read_bytes (blob, size);<br>
+   if (bytes == NULL)<br>
+      return;<br>
+<br>
+   memcpy (dest, bytes, size);<br>
+}<br>
+<br>
+uint32_t<br>
</span>+blob_read_uint32 (struct blob_reader *blob)<br>
+{<br>
+   uint32_t ret;<br>
<span class="">+   int size = sizeof(ret);<br>
+<br>
+   align_blob_reader(blob, size);<br>
+<br>
</span><span class="">+   if (! ensure_can_read(blob, size))<br>
+      return 0;<br>
</span><span class="">+<br>
+   ret = *((uint32_t*) blob->current);<br>
+<br>
+   blob->current += size;<br>
+<br>
+   return ret;<br>
+}<br>
+<br>
+uint64_t<br>
</span>+blob_read_uint64 (struct blob_reader *blob)<br>
+{<br>
+   uint64_t ret;<br>
<span class="">+   int size = sizeof(ret);<br>
+<br>
+   align_blob_reader(blob, size);<br>
+<br>
</span><span class="">+   if (! ensure_can_read(blob, size))<br>
+      return 0;<br>
</span><span class="">+<br>
+   ret = *((uint64_t*) blob->current);<br>
+<br>
+   blob->current += size;<br>
+<br>
+   return ret;<br>
+}<br>
+<br>
+intptr_t<br>
</span>+blob_read_intptr (struct blob_reader *blob)<br>
+{<br>
+   intptr_t ret;<br>
<span class="">+   int size = sizeof(ret);<br>
+<br>
+   align_blob_reader(blob, size);<br>
+<br>
</span><span class="">+   if (! ensure_can_read(blob, size))<br>
+      return 0;<br>
</span><span class="">+<br>
+   ret = *((intptr_t *) blob->current);<br>
+<br>
+   blob->current += size;<br>
+<br>
+   return ret;<br>
+}<br></span></blockquote><div><br>Something to tuck in the back of your brain in case you have a need to grow this datastructure: The above write functions could be generated with three invocations of a macro that takes a function name and a type.  That would prevent copy-and-paste typos.  For only 3 functions, this probably isn't worth it.  However, if we ever expand this to also include uint8, uint16, float, and double, that might not be a bad idea.  Same for the writing functions.<br><br></div><div>Other than the couple comments here and the alignment thing (different e-mail).<br><br>Reviewed-by: Jason Ekstrand <<a href="mailto:jason.ekstrand@intel.com">jason.ekstrand@intel.com</a>><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
+<br>
+char *<br>
</span><span class="">+blob_read_string (struct blob_reader *blob)<br>
+{<br>
+   int size;<br>
+   char *ret;<br>
+   uint8_t *nul;<br>
+<br>
</span>+   /* If we're already at the end, then this is an overrun. */<br>
+   if (blob->current >= blob->end) {<br>
<span class="">+      blob->overrun = true;<br>
</span>+      return NULL;<br>
+   }<br>
+<br>
+   /* Similarly, if there is no zero byte in the data remaining in this blob,<br>
+    * we also consider that an overrun. */<br>
<span class="">+   nul = memchr (blob->current, 0, blob->end - blob->current);<br>
</span><span class="">+<br>
+   if (nul == NULL) {<br>
</span>+      blob->overrun = true;<br>
+      return NULL;<br>
+   }<br>
<span class="">+<br>
+   size = nul - blob->current + 1;<br>
+<br>
</span>+   assert (ensure_can_read(blob, size));<br>
<span class="">+<br>
+   ret = (char *) blob->current;<br>
+<br>
+   blob->current += size;<br>
+<br>
+   return ret;<br>
+}<br>
diff --git a/src/glsl/blob.h b/src/glsl/blob.h<br>
new file mode 100644<br>
</span>index 0000000..8d0d959<br>
--- /dev/null<br>
+++ b/src/glsl/blob.h<br>
@@ -0,0 +1,245 @@<br>
<span class="">+/*<br>
+ * Copyright © 2014 Intel Corporation<br>
+ *<br>
</span><span class="">+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
</span><div><div class="h5">+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS<br>
+ * IN THE SOFTWARE.<br>
+ */<br>
+<br>
+#pragma once<br>
+#ifndef BLOB_H<br>
+#define BLOB_H<br>
+<br>
+#ifdef __cplusplus<br>
+extern "C" {<br>
+#endif<br>
+<br>
+#include <stdint.h><br>
+<br>
+/* The blob functions implement a simple, low-level API for serializing and<br>
+ * deserializing.<br>
+ *<br>
+ * All objects written to a blob will be serialized directly, (without any<br>
+ * additional meta-data to describe the data written). Therefore, it is the<br>
+ * caller's responsibility to ensure that any data can be read later, (either<br>
+ * by knowing exactly what data is expected, or by writing to the blob<br>
+ * sufficient meta-data to describe what has been written).<br>
+ *<br>
+ * A blob is efficient in that it dynamically grows by doubling in size, so<br>
+ * allocation costs are logarithmic.<br>
+ */<br>
+<br>
+/* The allocated size is the number of bytes that have actually been allocated<br>
+ * for the "data" array, while "size" is the number of bytes actually used to<br>
+ * store data.<br>
+ */<br>
+struct blob {<br>
+   uint8_t *data;<br>
+   size_t allocated;<br>
+   size_t size;<br>
+};<br>
+<br>
+/* When done reading, the caller can ensure that everything was consumed by<br>
+ * checking the following:<br>
+ *<br>
+ *   1. blob->data should be equal to blob->end, (if not, too little was<br>
+ *      read).<br>
+ *<br>
+ *   2. blob->overrun should be false, (otherwise, too much was read).<br>
+ */<br>
+struct blob_reader {<br>
+   uint8_t *data;<br>
+   uint8_t *end;<br>
+   uint8_t *current;<br>
+   bool overrun;<br>
+};<br>
+<br>
+/**<br>
+ * Create a new, empty blob, belonging to \mem_ctx.<br>
+ *<br>
+ * \return The new blob, (or NULL in case of allocation failure).<br>
+ */<br>
+struct blob *<br>
+blob_create (void *mem_ctx);<br>
+<br>
+/**<br>
+ * Add some unstructured, fixed-size data to a blob.<br>
+ *<br>
+ * \return True unless allocation failed.<br>
+ */<br>
+bool<br>
+blob_write_bytes (struct blob *blob, const void *bytes, size_t to_write);<br>
+<br>
+/**<br>
+ * Reserve space in \blob for a number of bytes.<br>
+ *<br>
+ * Space will be allocated within the blob for these byes, but the bytes will<br>
+ * be left uninitialized. The caller is expected to use the return value to<br>
+ * write directly (and immediately) to these bytes.<br>
+ *<br>
+ * NOTE: The return value is valid immediately upon return, but can be<br>
+ * invalidated by any other call to a blob function. So the caller should call<br>
+ * blob_reserve_byes immediately before writing through the returned pointer.<br>
+ *<br>
+ * This function is intended to be used when interfacing with an existing API<br>
+ * that is not aware of the blob API, (so that blob_write_bytes cannot be<br>
+ * called).<br>
+ *<br>
+ * \return A pointer to space allocated within \blob to which \to_write bytes<br>
+ * can be written, (or NULL in case of any allocation error).<br>
+ */<br>
+uint8_t *<br>
+blob_reserve_bytes (struct blob *blob, size_t to_write);<br>
+<br>
+/**<br>
</div></div><div><div class="h5">+ * Add a uint32_t to a blob.<br>
+ *<br>
+ * Note: This function will only write to a uint32_t-aligned offset from the<br>
+ * beginning of the blob's data, so some padding bytes may be added to the<br>
+ * blob if this write follows some unaligned write (such as<br>
+ * blob_write_string).<br>
+ *<br>
+ * \return True unless allocation failed.<br>
+ */<br>
+bool<br>
+blob_write_uint32 (struct blob *blob, uint32_t value);<br>
+<br>
+/**<br>
+ * Add a uint64_t to a blob.<br>
+ *<br>
+ * Note: This function will only write to a uint64_t-aligned offset from the<br>
+ * beginning of the blob's data, so some padding bytes may be added to the<br>
+ * blob if this write follows some unaligned write (such as<br>
+ * blob_write_string).<br>
+ *<br>
+ * \return True unless allocation failed.<br>
+ */<br>
+bool<br>
+blob_write_uint64 (struct blob *blob, uint64_t value);<br>
+<br>
+/**<br>
+ * Add an intptr_t to a blob.<br>
+ *<br>
+ * Note: This function will only write to an intptr_t-aligned offset from the<br>
+ * beginning of the blob's data, so some padding bytes may be added to the<br>
+ * blob if this write follows some unaligned write (such as<br>
+ * blob_write_string).<br>
+ *<br>
+ * \return True unless allocation failed.<br>
+ */<br>
+bool<br>
+blob_write_intptr (struct blob *blob, intptr_t value);<br>
+<br>
+/**<br>
+ * Add a NULL-terminated string to a blob, (including the NULL terminator).<br>
+ *<br>
+ * \return True unless allocation failed.<br>
+ */<br>
+bool<br>
+blob_write_string (struct blob *blob, const char *str);<br>
+<br>
+/**<br>
+ * Start reading a blob, (inintializing the contents of \blob for reading).<br>
+ *<br>
+ * After this call, the caller can use the various blob_read_* functions to<br>
+ * read elements from the data array.<br>
+ *<br>
+ * For all of the blob_read_* functions, if there is insufficient data<br>
+ * remaining, the functions will do nothing, (perhaps returning default values<br>
+ * such as 0). The caller can detect this by noting that the blob_reader's<br>
+ * current value is unchanged before and after the call.<br>
+ */<br>
</div></div>+void<br>
<span class="im HOEnZb">+blob_reader_init (struct blob_reader *blob, uint8_t *data, size_t size);<br>
+<br>
+/**<br>
+ * Read some unstructured, fixed-size data from the current location, (and<br>
+ * update the current location to just past this data).<br>
+ *<br>
+ * The memory returned belongs to the data underlying the blob reader. The<br>
+ * caller must copy the data in order to use it after the lifetime of the data<br>
+ * underlying the blob reader.<br>
+ *<br>
+ * \return The bytes read (see note above about memory lifetime).<br>
+ */<br>
</span><span class="im HOEnZb">+void *<br>
+blob_read_bytes (struct blob_reader *blob, size_t size);<br>
+<br>
</span><div class="HOEnZb"><div class="h5">+/**<br>
+ * Read some unstructured, fixed-size data from the current location, copying<br>
+ * it to \dest (and update the current location to just past this data)<br>
+ */<br>
+void<br>
+blob_copy_bytes (struct blob_reader *blob, uint8_t *dest, size_t size);<br>
+<br>
+/**<br>
+ * Read a uint32_t from the current location, (and update the current location<br>
+ * to just past this uint32_t).<br>
+ *<br>
+ * Note: This function will only read from a uint32_t-aligned offset from the<br>
+ * beginning of the blob's data, so some padding bytes may be skipped.<br>
+ *<br>
+ * \return The uint32_t read<br>
+ */<br>
+uint32_t<br>
+blob_read_uint32 (struct blob_reader *blob);<br>
+<br>
+/**<br>
+ * Read a uint64_t from the current location, (and update the current location<br>
+ * to just past this uint64_t).<br>
+ *<br>
+ * Note: This function will only read from a uint64_t-aligned offset from the<br>
+ * beginning of the blob's data, so some padding bytes may be skipped.<br>
+ *<br>
+ * \return The uint64_t read<br>
+ */<br>
+uint64_t<br>
+blob_read_uint64 (struct blob_reader *blob);<br>
+<br>
+/**<br>
+ * Read an intptr_t value from the current location, (and update the<br>
+ * current location to just past this intptr_t).<br>
+ *<br>
+ * Note: This function will only read from an intptr_t-aligned offset from the<br>
+ * beginning of the blob's data, so some padding bytes may be skipped.<br>
+ *<br>
+ * \return The intptr_t read<br>
+ */<br>
+intptr_t<br>
+blob_read_intptr (struct blob_reader *blob);<br>
+<br>
+/**<br>
+ * Read a NULL-terminated string from the current location, (and update the<br>
+ * current location to just past this string).<br>
+ *<br>
+ * The memory returned belongs to the data underlying the blob reader. The<br>
+ * caller must copy the string in order to use the string after the lifetime<br>
+ * of the data underlying the blob reader.<br>
+ *<br>
+ * \return The string read (see note above about memory lifetime). However, if<br>
+ * there is no NULL byte remaining within the blob, this function returns<br>
+ * NULL.<br>
+ */<br>
+char *<br>
</div></div><span class="im HOEnZb">+blob_read_string (struct blob_reader *blob);<br>
+<br>
</span><div class="HOEnZb"><div class="h5">+#ifdef __cplusplus<br>
+}<br>
+#endif<br>
+<br>
+#endif /* BLOB_H */<br>
--<br>
2.1.1<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev" target="_blank">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</div></div></blockquote></div></div></div>