Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added igbinary.

  • Loading branch information...
commit ad4d302a438c51614645fc13f0f173a14beca4b1 1 parent 40780e0
@nicolasff nicolasff authored
Showing with 5,713 additions and 54 deletions.
  1. +1 −1  config.m4
  2. +53 −0 igbinary/.gitignore
  3. +28 −0 igbinary/COPYING
  4. +8 −0 igbinary/CREDITS
  5. +10 −0 igbinary/ChangeLog
  6. 0  igbinary/EXPERIMENTAL
  7. +8 −0 igbinary/NEWS
  8. +95 −0 igbinary/README
  9. +39 −0 igbinary/benchmark/album1.php
  10. +78 −0 igbinary/benchmark/album2.php
  11. +29 −0 igbinary/benchmark/matrix.php
  12. +3 −0  igbinary/benchmark/scalar.php
  13. +3 −0  igbinary/benchmark/simple.php
  14. +141 −0 igbinary/benchmark/tests.php
  15. +40 −0 igbinary/config.m4
  16. +97 −0 igbinary/hash.h
  17. +183 −0 igbinary/hash_function.c
  18. +26 −0 igbinary/hash_function.h
  19. +261 −0 igbinary/hash_si.c
  20. +1,972 −0 igbinary/igbinary.c
  21. +33 −0 igbinary/igbinary.h
  22. +68 −0 igbinary/igbinary.php
  23. +6 −0 igbinary/igbinary.php.ini
  24. +52 −0 igbinary/igbinary.spec
  25. +72 −0 igbinary/package.xml
  26. +98 −0 igbinary/php_igbinary.h
  27. +8 −0 igbinary/tags.sh
  28. +21 −0 igbinary/tests/001.phpt
  29. +37 −0 igbinary/tests/002.phpt
  30. +41 −0 igbinary/tests/003.phpt
  31. +61 −0 igbinary/tests/004.phpt
  32. +37 −0 igbinary/tests/005.phpt
  33. +41 −0 igbinary/tests/006.phpt
  34. +45 −0 igbinary/tests/007.phpt
  35. +49 −0 igbinary/tests/008.phpt
  36. +78 −0 igbinary/tests/009.phpt
  37. +47 −0 igbinary/tests/010.phpt
  38. +55 −0 igbinary/tests/012.phpt
  39. +50 −0 igbinary/tests/013.phpt
  40. +38 −0 igbinary/tests/014.phpt
  41. +65 −0 igbinary/tests/015.phpt
  42. +65 −0 igbinary/tests/015b.phpt
  43. +62 −0 igbinary/tests/016.phpt
  44. +54 −0 igbinary/tests/017.phpt
  45. +82 −0 igbinary/tests/018.phpt
  46. +49 −0 igbinary/tests/019.phpt
  47. +44 −0 igbinary/tests/020.phpt
  48. +58 −0 igbinary/tests/021.phpt
  49. +51 −0 igbinary/tests/022.phpt
  50. +28 −0 igbinary/tests/023.phpt
  51. +108 −0 igbinary/tests/024.phpt
  52. +131 −0 igbinary/tests/025.phpt
  53. +124 −0 igbinary/tests/026.phpt
  54. +78 −0 igbinary/tests/027.phpt
  55. +111 −0 igbinary/tests/028.phpt
  56. +21 −0 igbinary/tests/029.phpt
  57. +52 −0 igbinary/tests/030.phpt
  58. +89 −0 igbinary/tests/031.phpt
  59. +80 −0 igbinary/tests/032.phpt
  60. +55 −0 igbinary/tests/033.phpt
  61. +38 −0 igbinary/tests/034.phpt
  62. +40 −0 igbinary/tests/035.phpt
  63. +43 −0 igbinary/tests/040.phpt
  64. +48 −0 igbinary/tests/041.phpt
  65. +41 −0 igbinary/tests/042.phpt
  66. +22 −0 library.c
  67. +21 −21 redis.c
  68. +41 −32 tests/TestRedis.php
View
2  config.m4
@@ -46,5 +46,5 @@ if test "$PHP_REDIS" != "no"; then
dnl
dnl PHP_SUBST(REDIS_SHARED_LIBADD)
- PHP_NEW_EXTENSION(redis, redis.c library.c redis_session.c, $ext_shared)
+ PHP_NEW_EXTENSION(redis, redis.c library.c redis_session.c igbinary/igbinary.c igbinary/hash_si.c igbinary/hash_function.c, $ext_shared)
fi
View
53 igbinary/.gitignore
@@ -0,0 +1,53 @@
+*.swp
+*.lo
+*.la
+.deps
+.libs
+Makefile
+Makefile.fragments
+Makefile.global
+Makefile.objects
+acinclude.m4
+aclocal.m4
+autom4te.cache
+build
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.nice
+config.status
+config.sub
+configure
+configure.in
+conftest
+conftest.c
+include
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+modules
+scan_makefile_in.awk
+*.dsw
+*.plg
+*.opt
+*.ncb
+Release
+Release_inline
+Debug
+Release_TS
+Release_TSDbg
+Release_TS_inline
+Debug_TS
+run-tests.php
+cscope.out
+tests/*.out
+tests/*.php
+tests/*.mem
+tests/*.diff
+tests/*.log
+tests/*.exp
+tmp-php.ini
View
28 igbinary/COPYING
@@ -0,0 +1,28 @@
+Copyright (c) 2008 Sulake Dynamoid Oy
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+- Neither the name of the 'igbinary' nor the names of its contributors may
+ be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
View
8 igbinary/CREDITS
@@ -0,0 +1,8 @@
+igbinary is produced for IRC-Galleria (http://irc-galleria.net, a finnish community site)
+by Sulake Dynamoid Oy and its employees.
+
+- Igbinary
+Oleg Grenrus <oleg.grenrus@dynamoid.com>
+
+- Hash functions
+Bob Jenkins <bob_jenkins@burtleburtle.net>
View
10 igbinary/ChangeLog
@@ -0,0 +1,10 @@
+2008-06-29 Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ * config.m4: fixed CFLAGS
+ * igbinary.c: do not init object & string hashes if serializing scalars.
+ unserialize_callback_func support.
+ igbinary_serialize and igbinary_unserialize for the external use.
+ * intbinary.h: header for external use
+ * tests/022.phpt: unserialize_callback_func test
+ * tests/023.phpt: resource serializes to null test
+ * tests/002.phpt tests/003.phpt tests/004.phpt tests/005.phpt tests/006.phpt: == -> ===
+ * tests/015b.phpt: pointed out http://bugs.net/bug.php?id=45406
View
0  igbinary/EXPERIMENTAL
No changes.
View
8 igbinary/NEWS
@@ -0,0 +1,8 @@
+1.0.1 2008-07-05
+========
+* unserialize_callback_func support
+* slight speedup when serializing scalars
+
+1.0.0 2008-06-25
+========
+* Public version
View
95 igbinary/README
@@ -0,0 +1,95 @@
+igbinary
+========
+
+What it is?
+-----------
+
+Igbinary is a drop in replacement for the standard php serializer. Instead of
+time and space consuming textual representation, igbinary stores php data
+structures in a compact binary form. Savings are significant when using
+memcached or similar memory based storages for serialized data.
+
+But where does the name "igbinary" come from? There was once a similar project
+called fbinary but it has disappeared from the Internet. Its architecture
+wasn't very clean either. IG is an abbreviation for a finnish social networking
+site IRC-Galleria (http://irc-galleria.net/)
+
+Supports:
+- PHP5 (tested with 5.2.4 and 5.2.5)
+- Same data types as the standard php serializer:
+ null, bool, int, float, string, array and objects.
+- __autoload & unserialize_callback_func
+- __sleep & __wakeup
+- Serializable -interface
+- Data portability between platforms (32/64bit, endianess)
+- Tested on Linux amd64, Mac OSX x86 and NetBSD sparc64
+
+Why to use
+----------
+
+Storing complex php data structures like arrays of associative arrays in
+serialized form is not very space efficient. Igbinary uses two strategies to
+minimize size of the serialized form.
+
+Strings are stored only once by using a hash table. Arrays of associate arrays
+with very verbose keys are stored very compactly.
+
+Numerical values are stored in the smallest primitive data type available.
+123 = int8_t
+1234 = int16_t
+123456 = int32_t
+... and so on.
+
+How to use
+----------
+
+Add the following lines to your php.ini:
+
+# Load igbinary extension
+extension=igbinary.so
+
+# Use igbinary as session serializer
+session.serialize_handler=igbinary
+
+.. and in your php code replace serialize and unserialize function calls
+with igbinary_serialize and igbinary_unserialize.
+
+Installing
+----------
+
+Note:
+Sometimes you may have to substitute phpize with phpize5.
+is such cases you will probably want to add "--with-php-config=.../php-config5" as
+an option to configure.
+
+Compiling:
+
+1. phpize
+
+If you use GCC:
+2. ./configure CFLAGS="-O2 -g" --enable-igbinary
+
+If you use ICC (Intel C Compiler)
+2. ./configure CFLAGS=" -no-prec-div -O3 -xO -unroll2 -g" CC=icc --enable-igbinary
+
+3. make
+4. ( make test)
+5. make install
+6. igbinary.so is installed in the default extensions directory
+
+Bugs & Contributions
+--------------------
+
+To report bugs, or to contribute fixes and improvements send email to
+opensource@dynamoid.com
+
+Use from other extensions
+-------------------------
+
+If you are writing own extension and it is possible to use igbinary.
+Igbinary installs own header igbinary.h to the ext/igbinary/igbinary.h.
+There are just two straighforward functions: igbinary_serialize and igbinary_unserialize.
+Look at the igbinary.h for prototypes and more information.
+
+Use PHP_ADD_EXTENSION_DEP(yourextension, igbinary) in config.m4 if someone wants
+compile both of them statically into php.
View
39 igbinary/benchmark/album1.php
@@ -0,0 +1,39 @@
+<?php
+
+$data = array(
+ 'euro2008' => array(
+ 'title' => 'Euro 2008',
+ 'description' => 'Pictures from UEFA EURO CUP 2008',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 1),
+ array('width' => 600, 'height' => '400', 'id' => 2),
+ array('width' => 300, 'height' => '200', 'id' => 3),
+ array('width' => 300, 'height' => '200', 'id' => 4),
+ )
+ ),
+
+ 'summer2008' => array(
+ 'title' => 'Summer 2008',
+ 'description' => 'Summer \o/',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 5),
+ array('width' => 600, 'height' => '400', 'id' => 6),
+ array('width' => 300, 'height' => '200', 'id' => 7),
+ array('width' => 300, 'height' => '200', 'id' => 8),
+ )
+ ),
+
+ 'summer2007' => array(
+ 'title' => 'Summer 2007',
+ 'description' => 'Summer \o/',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 9),
+ array('width' => 600, 'height' => '400', 'id' => 10),
+ array('width' => 300, 'height' => '200', 'id' => 11),
+ array('width' => 300, 'height' => '200', 'id' => 12),
+ array('width' => 300, 'height' => '200', 'id' => 13),
+ )
+ )
+);
+
+?>
View
78 igbinary/benchmark/album2.php
@@ -0,0 +1,78 @@
+<?php
+
+$data = array(
+ 'euro2008' => array(
+ 'title' => 'Euro 2008',
+ 'description' => 'Pictures from UEFA EURO CUP 2008',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 1),
+ array('width' => 600, 'height' => '400', 'id' => 2),
+ array('width' => 300, 'height' => '200', 'id' => 3),
+ array('width' => 300, 'height' => '200', 'id' => 4),
+ )
+ ),
+
+ 'summer2008' => array(
+ 'title' => 'Summer 2008',
+ 'description' => 'Summer \o/',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 5),
+ array('width' => 600, 'height' => '400', 'id' => 6),
+ array('width' => 300, 'height' => '200', 'id' => 7),
+ array('width' => 300, 'height' => '200', 'id' => 8),
+ )
+ ),
+
+ 'summer2007' => array(
+ 'title' => 'Summer 2007',
+ 'description' => 'Summer \o/',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 9),
+ array('width' => 600, 'height' => '400', 'id' => 10),
+ array('width' => 300, 'height' => '200', 'id' => 11),
+ array('width' => 300, 'height' => '200', 'id' => 12),
+ array('width' => 300, 'height' => '200', 'id' => 13),
+ )
+ ),
+
+ 'summer2006' => array(
+ 'title' => 'Summer 2006',
+ 'description' => 'Summer \o/',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 14),
+ array('width' => 600, 'height' => '400', 'id' => 15),
+ array('width' => 300, 'height' => '200', 'id' => 16),
+ array('width' => 300, 'height' => '200', 'id' => 17),
+ array('width' => 300, 'height' => '200', 'id' => 18),
+ array('width' => 300, 'height' => '200', 'id' => 19),
+ )
+ ),
+
+ 'summer2005' => array(
+ 'title' => 'Summer 2005',
+ 'description' => 'Summer \o/',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 20),
+ array('width' => 600, 'height' => '400', 'id' => 21),
+ array('width' => 300, 'height' => '200', 'id' => 22),
+ array('width' => 300, 'height' => '200', 'id' => 23),
+ array('width' => 300, 'height' => '200', 'id' => 24),
+ array('width' => 300, 'height' => '200', 'id' => 25),
+ )
+ ),
+
+ 'summer2004' => array(
+ 'title' => 'Summer 2004',
+ 'description' => 'Summer \o/',
+ 'pics' => array(
+ array('width' => 600, 'height' => '400', 'id' => 26),
+ array('width' => 600, 'height' => '400', 'id' => 27),
+ array('width' => 300, 'height' => '200', 'id' => 28),
+ array('width' => 300, 'height' => '200', 'id' => 29),
+ array('width' => 300, 'height' => '200', 'id' => 30),
+ array('width' => 300, 'height' => '200', 'id' => 31),
+ )
+ )
+);
+
+?>
View
29 igbinary/benchmark/matrix.php
@@ -0,0 +1,29 @@
+<?php
+$data = array(
+ 1 => array(
+ 2 => array(3, 4, 5, 6),
+ 3 => array(40, 50, 60, 70),
+ 4 => array(500, 600, 700, 800),
+ 2 => array(300000, 400000, 500000, 600000),
+ 3 => array(400, 500, 600, 700),
+ 4 => array(5000, 6000, 7000, 8000),
+ ),
+ 2 => array(
+ 2 => array(3, 4, 5, 6),
+ 3 => array(40, 50, 60, 70),
+ 4 => array(500, 600, 700, 800),
+ 2 => array(300000, 400000, 500000, 600000),
+ 3 => array(400, 500, 600, 700),
+ 4 => array(5000, 6000, 7000, 8000),
+ ),
+ 3 => array(
+ 2 => array(3, 4, 5, 6),
+ 3 => array(40, 50, 60, 70),
+ 4 => array(500, 600, 700, 800),
+ 2 => array(300000, 400000, 500000, 600000),
+ 3 => array(400, 500, 600, 700),
+ 4 => array(5000, 6000, 7000, 8000),
+ )
+);
+
+?>
View
3  igbinary/benchmark/scalar.php
@@ -0,0 +1,3 @@
+<?php
+$data = 42;
+?>
View
3  igbinary/benchmark/simple.php
@@ -0,0 +1,3 @@
+<?php
+$data = array('foo' => 'bar', 'bar' => 'foo');
+?>
View
141 igbinary/benchmark/tests.php
@@ -0,0 +1,141 @@
+<?php
+
+if (!extension_loaded('igbinary')) {
+ dl('igbinary.' . PHP_SHLIB_SUFFIX);
+}
+
+function test_speed_print($testname, $php, $igbinary) {
+ sort($php);
+ sort($igbinary);
+ reset($php);
+ reset($igbinary);
+
+ $phptime = ($php[1] + $php[2] + $php[3]) / 3;
+ $igbinarytime = ($igbinary[1] + $igbinary[2] + $igbinary[3]) / 3;
+
+ $phpstr = sprintf('%.3f', $php[0]);
+ $igbinarystr = sprintf('%.3f', $igbinary[0]);
+
+ for ($i = 1; $i < 5; ++$i) {
+ $phpstr .= sprintf(' %.3f', $php[$i]);
+ $igbinarystr .= sprintf(' %.3f', $igbinary[$i]);
+
+ }
+
+ echo $testname, ":\n";
+ printf(" php : %6.3f [%s]\n", $phptime, $phpstr);
+ printf(" igbinary: %6.3f %6.02f%% [%s]\n", $igbinarytime, $igbinarytime * 100 / $phptime, $igbinarystr);
+}
+
+function test_speed_serialize_php($data, $loops) {
+ $start = microtime(true);
+ for ($i = 0; $i < $loops; ++$i) {
+ $tmp = serialize($data);
+ }
+ $end = microtime(true);
+ return $end - $start;
+}
+
+function test_speed_serialize_igbinary($data, $loops) {
+ $start = microtime(true);
+ for ($i = 0; $i < $loops; ++$i) {
+ $tmp = igbinary_serialize($data);
+ }
+ $end = microtime(true);
+ return $end - $start;
+}
+
+function test_speed_serialize($testname, $data, $loops) {
+ $php = array();
+ $igbinary = array();
+
+ for ($i = 0; $i < 5; ++$i) {
+ $php[] = test_speed_serialize_php($data, $loops);
+ $igbinary[] = test_speed_serialize_igbinary($data, $loops);
+ }
+
+ test_speed_print($testname, $php, $igbinary);
+}
+
+function test_speed_unserialize_php($data, $loops) {
+ $serdata = serialize($data);
+ $start = microtime(true);
+ for ($i = 0; $i < $loops; ++$i) {
+ $tmp = unserialize($serdata);
+ }
+ $end = microtime(true);
+ return $end - $start;
+}
+
+function test_speed_unserialize_igbinary($data, $loops) {
+ $serdata = igbinary_serialize($data);
+ $start = microtime(true);
+ for ($i = 0; $i < $loops; ++$i) {
+ $tmp = igbinary_unserialize($serdata);
+ }
+ $end = microtime(true);
+ return $end - $start;
+}
+
+function test_speed_unserialize($testname, $data, $loops) {
+ $php = array();
+ $igbinary = array();
+
+ for ($i = 0; $i < 5; ++$i) {
+ $php[] = test_speed_unserialize_php($data, $loops);
+ $igbinary[] = test_speed_unserialize_igbinary($data, $loops);
+ }
+
+ test_speed_print($testname, $php, $igbinary);
+}
+
+
+function test_space($testname, $data) {
+ $phplen = strlen(serialize($data));
+ $igbinarylen = strlen(igbinary_serialize($data));
+
+ printf(" php : %5u\n", $phplen);
+ printf(" igbinary: %5u (%.02f%%)\n", $igbinarylen, $igbinarylen * 100 / $phplen);
+}
+
+function usage() {
+ global $argv;
+
+ die('Usage: php '.$argv[0].' space|serialize|unserialize [data] [loops]'."\n");
+}
+
+if (count($argv) < 3) {
+ usage();
+}
+
+$t = $argv[1];
+$i = $argv[2];
+
+$loops = 100000;
+if (count($argv) > 3) {
+ $loops = (int) $argv[3];
+
+ if ($loops < 100 || $loops > 1000000) {
+ $loops = 100000;
+ }
+}
+
+if ((@include($i . '.php')) != 1) {
+ usage();
+}
+
+switch ($t) {
+ case 'space':
+ test_space($i, $data);
+ break;
+ case 'serialize':
+ test_speed_serialize($i, $data, $loops);
+ break;
+ case 'unserialize':
+ test_speed_unserialize($i, $data, $loops);
+ break;
+ default:
+ usage();
+}
+
+?>
View
40 igbinary/config.m4
@@ -0,0 +1,40 @@
+dnl $Id: config.m4,v 1.7 2008/07/03 17:05:57 phadej Exp $
+dnl config.m4 for extension igbinary
+
+dnl Comments in this file start with the string 'dnl'.
+dnl Remove where necessary. This file will not work
+dnl without editing.
+
+dnl If your extension references something external, use with:
+
+dnl PHP_ARG_WITH(igbinary, for igbinary support,
+dnl Make sure that the comment is aligned:
+dnl [ --with-igbinary Include igbinary support])
+
+dnl Otherwise use enable:
+
+PHP_ARG_ENABLE(igbinary, whether to enable igbinary support,
+ [ --enable-igbinary Enable igbinary support])
+
+if test "$PHP_IGBINARY" != "no"; then
+ AC_CHECK_HEADERS([stdbool.h],, AC_MSG_ERROR([stdbool.h not exists]))
+ AC_CHECK_HEADERS([stddef.h],, AC_MSG_ERROR([stddef.h not exists]))
+ AC_CHECK_HEADERS([stdint.h],, AC_MSG_ERROR([stdint.h not exists]))
+
+ AC_CHECK_SIZEOF([long])
+
+ dnl GCC
+ AC_MSG_CHECKING(compiler type)
+ if test ! -z "`$CC --version | grep -i GCC`"; then
+ AC_MSG_RESULT(gcc)
+ PHP_IGBINARY_CFLAGS="-Wall -Wpointer-arith -Wmissing-prototypes -Wstrict-prototypes -Wcast-align -Wshadow -Wwrite-strings -Wswitch -Winline -finline-limit=10000 --param large-function-growth=10000 --param inline-unit-growth=10000"
+ elif test ! -z "`$CC --version | grep -i ICC`"; then
+ AC_MSG_RESULT(icc)
+ PHP_IGBINARY_CFLAGS="-no-prec-div -O3 -x0 -unroll2"
+ else
+ AC_MSG_RESULT(other)
+ fi
+
+ PHP_INSTALL_HEADERS([ext/igbinary], [igbinary.h])
+ PHP_NEW_EXTENSION(igbinary, igbinary.c hash_si.c hash_function.c, $ext_shared,, $PHP_IGBINARY_CFLAGS)
+fi
View
97 igbinary/hash.h
@@ -0,0 +1,97 @@
+/*
+ * Author: Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ *
+ * $Id: hash.h,v 1.5 2008/07/01 17:02:18 phadej Exp $
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/** Key/value pair of hash_si.
+ * @author Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ * @see hash_si
+ */
+struct hash_si_pair
+{
+ char *key; /**< Pointer to key. */
+ size_t key_len; /**< Key length. */
+ uint32_t value; /**< Value. */
+};
+
+/** Hash-array.
+ * Like c++ map<char *, int32_t>.
+ * Current implementation uses linear probing.
+ * @author Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ */
+struct hash_si {
+ size_t size; /**< Allocated size of array. */
+ size_t used; /**< Used size of array. */
+ struct hash_si_pair *data; /**< Pointer to array or pairs of data. */
+};
+
+/** Inits hash_si structure.
+ * @param h pointer to hash_si struct.
+ * @param size initial size of the hash array.
+ * @return 0 on success, 1 else.
+ */
+int hash_si_init (struct hash_si *h, size_t size);
+
+/** Frees hash_si structure.
+ * Doesn't call free(h).
+ * @param h pointer to hash_si struct.
+ */
+void hash_si_deinit (struct hash_si *h);
+
+/** Inserts value into hash_si.
+ * @param h Pointer to hash_si struct.
+ * @param key Pointer to key.
+ * @param key_len Key length.
+ * @param value Value.
+ * @return 0 on success, 1 or 2 else.
+ */
+int hash_si_insert (struct hash_si *h, const char *key, size_t key_len, uint32_t value);
+
+/** Finds value from hash_si.
+ * Value returned thru value param.
+ * @param h Pointer to hash_si struct.
+ * @param key Pointer to key.
+ * @param key_len Key length.
+ * @param[out] value Found value.
+ * @return 0 if found, 1 if not.
+ */
+int hash_si_find (struct hash_si *h, const char *key, size_t key_len, uint32_t * value);
+
+/** Remove value from hash_si.
+ * Removed value is available thru value param.
+ * @param h Pointer to hash_si struct.
+ * @param key Pointer to key.
+ * @param key_len Key length.
+ * @param[out] value Removed value.
+ * @return 0 ivalue removed, 1 if not existed.
+ */
+int hash_si_remove (struct hash_si *h, const char *key, size_t key_len, uint32_t * value);
+
+/** Travarses hash_si.
+ * Calls traverse_function on every item. Traverse function should not modify hash
+ * @param h Pointer to hash_si struct.
+ * @param traverse_function Function to call on every item of hash_si.
+ */
+void hash_si_traverse (struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value));
+
+/** Returns size of hash_si.
+ * @param h Pointer to hash_si struct.
+ * @return Size of hash_si.
+ */
+size_t hash_si_size (struct hash_si *h);
+
+/** Returns capacity of hash_si.
+ * @param h Pointer to hash_si struct.
+ * @return Capacity of hash_si.
+ */
+size_t hash_si_capacity (struct hash_si *h);
+
+#endif /* HASH_H */
View
183 igbinary/hash_function.c
@@ -0,0 +1,183 @@
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+*/
+
+#include <sys/param.h> /* attempt to define endianness */
+#ifdef linux
+# include <endian.h> /* attempt to define endianness */
+#endif
+
+#include "hash_function.h"
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta. I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche. There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a. The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism. Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism. I did what I could. Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different. This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Two keys differing by one or two bits will have
+totally different hash values.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+
+By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable. Do NOT use for cryptographic purposes.
+-------------------------------------------------------------------------------
+*/
+
+uint32_t hash_function( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c; /* internal state */
+ const uint8_t *k = (const uint8_t *)key;
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : return c;
+ }
+
+ final(a,b,c);
+ return c;
+}
View
26 igbinary/hash_function.h
@@ -0,0 +1,26 @@
+/*
+ * Author: Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ *
+ * $Id: hash_function.h,v 1.4 2008/07/01 17:02:18 phadej Exp $
+ */
+
+#ifndef HASH_FUNCTION_H
+#define HASH_FUNCTION_H
+
+#include <stdint.h> /* defines uint32_t etc */
+
+/**
+ * Hash function
+ *
+ * At this moment lookup3 by Bob Jerkins
+ *
+ * @param key key
+ * @param length key length
+ * @param initval hash init val
+ * @return hash value of key
+ * @see http://burtleburtle.net/bob/hash/index.html
+ * @author Bob Jerkins <bob_jenkins@burtleburtle.net>
+ */
+uint32_t hash_function(const void *key, size_t length, uint32_t initval);
+
+#endif /* HASH_FUNCTION_H */
View
261 igbinary/hash_si.c
@@ -0,0 +1,261 @@
+/*
+ * Author: Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ *
+ * $Id: hash_si.c,v 1.5 2008/07/01 17:02:18 phadej Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <assert.h>
+
+#include "hash.h"
+#include "hash_function.h"
+
+/* {{{ nextpow2 */
+/** Next power of 2.
+ * @param n Integer.
+ * @return next to n power of 2 .
+ */
+inline static uint32_t nextpow2(uint32_t n) {
+ uint32_t m = 1;
+ while (m < n) {
+ m = m << 1;
+ }
+
+ return m;
+}
+/* }}} */
+/* {{{ hash_si_init */
+int hash_si_init(struct hash_si *h, size_t size) {
+ size = nextpow2(size);
+
+ h->size = size;
+ h->used = 0;
+ h->data = (struct hash_si_pair *) malloc(sizeof(struct hash_si_pair) * size);
+ if (h->data == NULL) {
+ return 1;
+ }
+
+ memset(h->data, 0, sizeof(struct hash_si_pair) * size);
+
+ return 0;
+}
+/* }}} */
+/* {{{ hash_si_deinit */
+void hash_si_deinit(struct hash_si *h) {
+ int i;
+
+ for (i = 0; i < h->size; i++) {
+ if (h->data[i].key != NULL) {
+ free(h->data[i].key);
+ }
+ }
+
+ free(h->data);
+
+ h->size = 0;
+ h->used = 0;
+}
+/* }}} */
+/* {{{ _hash_si_find */
+/** Returns index of key, or where it should be.
+ * @param h Pointer to hash_si struct.
+ * @param key Pointer to key.
+ * @param key_len Key length.
+ * @return index.
+ */
+inline static size_t _hash_si_find(struct hash_si *h, const char *key, size_t key_len) {
+ uint32_t hv;
+ size_t size;
+
+ assert(h != NULL);
+
+ size = h->size;
+ hv = hash_function(key, key_len, 0) & (h->size-1);
+
+ while (size > 0 &&
+ h->data[hv].key != NULL &&
+ (h->data[hv].key_len != key_len || memcmp(h->data[hv].key, key, key_len) != 0)) {
+ /* linear prob */
+ hv = (hv + 1) & (h->size-1);
+ size--;
+ }
+
+ return hv;
+}
+/* }}} */
+/* {{{ hash_si_remove */
+int hash_si_remove(struct hash_si *h, const char *key, size_t key_len, uint32_t *value) {
+ uint32_t hv;
+ uint32_t j, k;
+
+ assert(h != NULL);
+
+ hv = _hash_si_find(h, key, key_len);
+
+ /* dont exists */
+ if (h->data[hv].key == NULL) {
+ return 1;
+ }
+
+ h->used--;
+
+ free(h->data[hv].key);
+
+ if (value != NULL)
+ *value = h->data[hv].value;
+
+ j = (hv + 1) & (h->size-1);
+ while (h->data[j].key != NULL) {
+ k = hash_function(h->data[j].key, strlen(h->data[j].key), 0) & (h->size-1);
+ if ((j > hv && (k <= hv || k > j)) || (j < hv && (k <= hv && k > j))) {
+ h->data[hv].key = h->data[j].key;
+ h->data[hv].key_len = h->data[j].key_len;
+ h->data[hv].value = h->data[j].value;
+
+ hv = j;
+ }
+ j = (j + 1) & (h->size-1);
+ }
+ h->data[hv].key = NULL;
+
+
+ return 0;
+/*
+ * loop
+ * j := (j+1) modulo num_slots
+ * if slot[j] is unoccupied
+ * exit loop
+ * k := hash(slot[j].key) modulo num_slots
+ * if (j > i and (k <= i or k > j)) or
+ * (j < i and (k <= i and k > j)) (note 2)
+ * slot[i] := slot[j]
+ * i := j
+ * mark slot[i] as unoccupied
+ *
+ * For all records in a cluster, there must be no vacant slots between their natural
+ * hash position and their current position (else lookups will terminate before finding
+ * the record). At this point in the pseudocode, i is a vacant slot that might be
+ * invalidating this property for subsequent records in the cluster. j is such a
+ * subsequent record. k is the raw hash where the record at j would naturally land in
+ * the hash table if there were no collisions. This test is asking if the record at j
+ * is invalidly positioned with respect to the required properties of a cluster now
+ * that i is vacant.
+ *
+ * Another technique for removal is simply to mark the slot as deleted. However
+ * this eventually requires rebuilding the table simply to remove deleted records.
+ * The methods above provide O(1) updating and removal of existing records, with
+ * occasional rebuilding if the high water mark of the table size grows.
+ */
+}
+/* }}} */
+/* {{{ hash_si_rehash */
+/** Rehash/resize hash_si.
+ * @param h Pointer to hash_si struct.
+ */
+inline static void hash_si_rehash(struct hash_si *h) {
+ uint32_t hv;
+ int i;
+ struct hash_si newh;
+
+ assert(h != NULL);
+
+ hash_si_init(&newh, h->size * 2);
+
+ for (i = 0; i < h->size; i++) {
+ if (h->data[i].key != NULL) {
+ hv = _hash_si_find(&newh, h->data[i].key, h->data[i].key_len);
+ newh.data[hv].key = h->data[i].key;
+ newh.data[hv].key_len = h->data[i].key_len;
+ newh.data[hv].value = h->data[i].value;
+ }
+ }
+
+ free(h->data);
+ h->data = newh.data;
+ h->size *= 2;
+}
+/* }}} */
+/* {{{ hash_si_insert */
+int hash_si_insert(struct hash_si *h, const char *key, size_t key_len, uint32_t value) {
+ uint32_t hv;
+
+ if (h->size / 4 * 3 < h->used + 1) {
+ hash_si_rehash(h);
+ }
+
+ hv = _hash_si_find(h, key, key_len);
+
+ if (h->data[hv].key == NULL) {
+ h->data[hv].key = (char *) malloc(key_len + 1);
+ if (h->data[hv].key == NULL) {
+ return 1;
+ }
+ memcpy(h->data[hv].key, key, key_len);
+ h->data[hv].key[key_len] = '\0';
+ h->data[hv].key_len = key_len;
+
+ h->used++;
+ } else {
+ return 2;
+ }
+
+ h->data[hv].value = value;
+
+ return 0;
+}
+/* }}} */
+/* {{{ hash_si_find */
+int hash_si_find(struct hash_si *h, const char *key, size_t key_len, uint32_t *value) {
+ uint32_t hv;
+
+ assert(h != NULL);
+
+ hv = _hash_si_find(h, key, key_len);
+
+ if (h->data[hv].key == NULL) {
+ return 1;
+ } else {
+ *value = h->data[hv].value;
+ return 0;
+ }
+}
+/* }}} */
+/* {{{ hash_si_traverse */
+void hash_si_traverse(struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)) {
+ int i;
+
+ assert(h != NULL && traverse_function != NULL);
+
+ for (i = 0; i < h->size; i++) {
+ if (h->data[i].key != NULL && traverse_function(h->data[i].key, h->data[i].key_len, h->data[i].value) != 1) {
+ return;
+ }
+ }
+}
+/* }}} */
+/* {{{ hash_si_size */
+size_t hash_si_size(struct hash_si *h) {
+ assert(h != NULL);
+
+ return h->used;
+}
+/* }}} */
+/* {{{ hash_si_capacity */
+size_t hash_si_capacity(struct hash_si *h) {
+ assert(h != NULL);
+
+ return h->size;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 2
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
View
1,972 igbinary/igbinary.c
@@ -0,0 +1,1972 @@
+/*
+ * Author: Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ *
+ * $Id: igbinary.c,v 1.33 2009/03/18 06:44:13 tricky Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "zend_dynamic_array.h"
+#include "zend_alloc.h"
+#include "ext/standard/info.h"
+#include "ext/session/php_session.h"
+#include "ext/standard/php_incomplete_class.h"
+#include "php_igbinary.h"
+
+#include "igbinary.h"
+
+#include <assert.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "hash.h"
+
+/** Session serializer function prototypes. */
+PS_SERIALIZER_FUNCS(igbinary);
+
+/* {{{ Types */
+enum igbinary_type {
+ /* 00 */ igbinary_type_null, /**< Null. */
+
+ /* 01 */ igbinary_type_ref8, /**< Array reference. */
+ /* 02 */ igbinary_type_ref16, /**< Array reference. */
+ /* 03 */ igbinary_type_ref32, /**< Array reference. */
+
+ /* 04 */ igbinary_type_bool_false, /**< Boolean true. */
+ /* 05 */ igbinary_type_bool_true, /**< Boolean false. */
+
+ /* 06 */ igbinary_type_long8p, /**< Long 8bit positive. */
+ /* 07 */ igbinary_type_long8n, /**< Long 8bit negative. */
+ /* 08 */ igbinary_type_long16p, /**< Long 16bit positive. */
+ /* 09 */ igbinary_type_long16n, /**< Long 16bit negative. */
+ /* 0a */ igbinary_type_long32p, /**< Long 32bit positive. */
+ /* 0b */ igbinary_type_long32n, /**< Long 32bit negative. */
+
+ /* 0c */ igbinary_type_double, /**< Double. */
+
+ /* 0d */ igbinary_type_string_empty, /**< Empty string. */
+
+ /* 0e */ igbinary_type_string_id8, /**< String id. */
+ /* 0f */ igbinary_type_string_id16, /**< String id. */
+ /* 10 */ igbinary_type_string_id32, /**< String id. */
+
+ /* 11 */ igbinary_type_string8, /**< String. */
+ /* 12 */ igbinary_type_string16, /**< String. */
+ /* 13 */ igbinary_type_string32, /**< String. */
+
+ /* 14 */ igbinary_type_array8, /**< Array. */
+ /* 15 */ igbinary_type_array16, /**< Array. */
+ /* 16 */ igbinary_type_array32, /**< Array. */
+
+ /* 17 */ igbinary_type_object8, /**< Object. */
+ /* 18 */ igbinary_type_object16, /**< Object. */
+ /* 19 */ igbinary_type_object32, /**< Object. */
+
+ /* 1a */ igbinary_type_object_id8, /**< Object string id. */
+ /* 1b */ igbinary_type_object_id16, /**< Object string id. */
+ /* 1c */ igbinary_type_object_id32, /**< Object string id. */
+
+ /* 1d */ igbinary_type_object_ser8, /**< Object serialized data. */
+ /* 1e */ igbinary_type_object_ser16, /**< Object serialized data. */
+ /* 1f */ igbinary_type_object_ser32, /**< Object serialized data. */
+
+ /* 20 */ igbinary_type_long64p, /**< Long 64bit positive. */
+ /* 21 */ igbinary_type_long64n, /**< Long 64bit negative. */
+
+ /* 22 */ igbinary_type_objref8, /**< Object reference. */
+ /* 23 */ igbinary_type_objref16, /**< Object reference. */
+ /* 24 */ igbinary_type_objref32, /**< Object reference. */
+
+ /* 25 */ igbinary_type_ref, /**< Simple reference */
+};
+
+/** Serializer data.
+ * @author Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ */
+struct igbinary_serialize_data {
+ uint8_t *buffer; /**< Buffer. */
+ size_t buffer_size; /**< Buffer size. */
+ size_t buffer_capacity; /**< Buffer capacity. */
+ bool scalar; /**< Serializing scalar. */
+ bool compact_strings; /**< Check for duplicate strings. */
+ struct hash_si strings; /**< Hash of already serialized strings. */
+ struct hash_si objects; /**< Hash of already serialized objects. */
+ int error; /**< Error number. Not used. */
+};
+
+/** String/len pair for the igbinary_unserializer_data.
+ * @author Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ * @see igbinary_unserialize_data.
+ */
+struct igbinary_unserialize_string_pair {
+ char *data; /**< Data. */
+ size_t len; /**< Data length. */
+};
+
+/** Unserializer data.
+ * @author Oleg Grenrus <oleg.grenrus@dynamoid.com>
+ */
+struct igbinary_unserialize_data {
+ uint8_t *buffer; /**< Buffer. */
+ size_t buffer_size; /**< Buffer size. */
+ size_t buffer_offset; /**< Current read offset. */
+
+ struct igbinary_unserialize_string_pair *strings; /**< Unserialized strings. */
+ size_t strings_count; /**< Unserialized string count. */
+ size_t strings_capacity; /**< Unserialized string array capacity. */
+
+ void **references; /**< Unserialized Arrays/Objects. */
+ size_t references_count; /**< Unserialized array/objects count. */
+ size_t references_capacity; /**< Unserialized array/object array capacity. */
+
+ int error; /**< Error number. Not used. */
+ smart_str string0_buf; /**< Temporary buffer for strings */
+};
+/* }}} */
+/* {{{ Serializing functions prototypes */
+inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar TSRMLS_DC);
+inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd TSRMLS_DC);
+
+inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd TSRMLS_DC);
+
+inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i TSRMLS_DC);
+inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i TSRMLS_DC);
+inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i TSRMLS_DC);
+inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i TSRMLS_DC);
+
+inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd TSRMLS_DC);
+inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b TSRMLS_DC);
+inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, long l TSRMLS_DC);
+inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d TSRMLS_DC);
+inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, char *s, size_t len TSRMLS_DC);
+inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len TSRMLS_DC);
+
+inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class TSRMLS_DC);
+inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object TSRMLS_DC);
+inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *ht, zend_class_entry *ce, bool incomplete_class TSRMLS_DC);
+inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, const char *name, size_t name_len TSRMLS_DC);
+inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC);
+
+static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC);
+/* }}} */
+/* {{{ Unserializing functions prototypes */
+inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd TSRMLS_DC);
+inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd TSRMLS_DC);
+
+inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd TSRMLS_DC);
+
+inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd TSRMLS_DC);
+inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd TSRMLS_DC);
+inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd TSRMLS_DC);
+inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd TSRMLS_DC);
+
+inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, long *ret TSRMLS_DC);
+inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, enum igbinary_type t, double *ret TSRMLS_DC);
+inline static int igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC);
+inline static int igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t, char **s, size_t *len TSRMLS_DC);
+
+inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, int object TSRMLS_DC);
+inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC);
+inline static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z, zend_class_entry *ce TSRMLS_DC);
+inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval **z TSRMLS_DC);
+
+static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval **z TSRMLS_DC);
+/* }}} */
+/* {{{ igbinary_functions[] */
+/** Exported php functions. */
+zend_function_entry igbinary_functions[] = {
+ PHP_FE(igbinary_serialize, NULL)
+ PHP_FE(igbinary_unserialize, NULL)
+ {NULL, NULL, NULL}
+};
+/* }}} */
+/* {{{ igbinary_module_entry */
+zend_module_entry igbinary_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ "igbinary",
+ igbinary_functions,
+ PHP_MINIT(igbinary),
+ PHP_MSHUTDOWN(igbinary),
+ NULL,
+ NULL,
+ PHP_MINFO(igbinary),
+#if ZEND_MODULE_API_NO >= 20010901
+ IGBINARY_VERSION, /* Replace with version number for your extension */
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+ZEND_DECLARE_MODULE_GLOBALS(igbinary)
+
+/* {{{ ZEND_GET_MODULE */
+#ifdef COMPILE_DL_IGBINARY
+ZEND_GET_MODULE(igbinary)
+#endif
+/* }}} */
+
+/* {{{ INI entries */
+PHP_INI_BEGIN()
+ STD_PHP_INI_BOOLEAN("igbinary.compact_strings", "1", PHP_INI_ALL, OnUpdateBool, compact_strings, zend_igbinary_globals, igbinary_globals)
+PHP_INI_END()
+/* }}} */
+
+/* {{{ php_igbinary_init_globals */
+static void php_igbinary_init_globals(zend_igbinary_globals *igbinary_globals) {
+ igbinary_globals->compact_strings = 1;
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION */
+PHP_MINIT_FUNCTION(igbinary) {
+ (void) type;
+ (void) module_number;
+ ZEND_INIT_MODULE_GLOBALS(igbinary, php_igbinary_init_globals, NULL);
+
+#if HAVE_PHP_SESSION
+ php_session_register_serializer("igbinary",
+ PS_SERIALIZER_ENCODE_NAME(igbinary),
+ PS_SERIALIZER_DECODE_NAME(igbinary));
+#endif
+ REGISTER_INI_ENTRIES();
+
+ return SUCCESS;
+}
+/* }}} */
+/* {{{ PHP_MSHUTDOWN_FUNCTION */
+PHP_MSHUTDOWN_FUNCTION(igbinary) {
+ (void) type;
+ (void) module_number;
+
+#ifdef ZTS
+ ts_free_id(igbinary_globals_id);
+#endif
+
+ /*
+ * unregister serializer?
+ */
+ UNREGISTER_INI_ENTRIES();
+
+ return SUCCESS;
+}
+/* }}} */
+/* {{{ PHP_MINFO_FUNCTION */
+PHP_MINFO_FUNCTION(igbinary) {
+ (void) zend_module;
+ php_info_print_table_start();
+ php_info_print_table_row(2, "igbinary support", "enabled");
+ php_info_print_table_row(2, "igbinary version", IGBINARY_VERSION);
+ php_info_print_table_row(2, "igbinary revision", "$Id: igbinary.c,v 1.33 2009/03/18 06:44:13 tricky Exp $");
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+/* {{{ int igbinary_serialize(uint8_t**, size_t*, zval*) */
+int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z TSRMLS_DC) {
+ struct igbinary_serialize_data igsd;
+
+ if (igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY TSRMLS_CC)) {
+ zend_error(E_WARNING, "igbinary_serialize: cannot init igsd");
+ return 1;
+ }
+
+ if (igbinary_serialize_header(&igsd TSRMLS_CC) != 0) {
+ igbinary_serialize_data_deinit(&igsd TSRMLS_CC);
+ return 1;
+ }
+
+ if (igbinary_serialize_zval(&igsd, z TSRMLS_CC) != 0) {
+ igbinary_serialize_data_deinit(&igsd TSRMLS_CC);
+ return 1;
+ }
+
+ *ret_len = igsd.buffer_size;
+ *ret = (uint8_t *) emalloc(igsd.buffer_size);
+ memcpy(*ret, igsd.buffer, igsd.buffer_size);
+
+ igbinary_serialize_data_deinit(&igsd TSRMLS_CC);
+
+ return 0;
+}
+/* }}} */
+/* {{{ int igbinary_unserialize(const uint8_t *, size_t, zval **) */
+int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval **z TSRMLS_DC) {
+ struct igbinary_unserialize_data igsd;
+
+ igbinary_unserialize_data_init(&igsd TSRMLS_CC);
+
+ igsd.buffer = (uint8_t *) buf;
+ igsd.buffer_size = buf_len;
+
+ if (igbinary_unserialize_header(&igsd TSRMLS_CC)) {
+ igbinary_unserialize_data_deinit(&igsd TSRMLS_CC);
+ return 1;
+ }
+
+ if (igbinary_unserialize_zval(&igsd, z TSRMLS_CC)) {
+ igbinary_unserialize_data_deinit(&igsd TSRMLS_CC);
+ return 1;
+ }
+
+ igbinary_unserialize_data_deinit(&igsd TSRMLS_CC);
+
+ return 0;
+}
+/* }}} */
+/* {{{ proto string igbinary_unserialize(mixed value) */
+PHP_FUNCTION(igbinary_unserialize) {
+ (void) return_value_ptr;
+ (void) this_ptr;
+ (void) return_value_used;
+
+ char *string;
+ int string_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &string_len) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if (string_len <= 0) {
+ RETURN_NULL();
+ }
+
+ if (igbinary_unserialize((uint8_t *) string, string_len, &return_value TSRMLS_CC)) {
+ RETURN_NULL();
+ }
+}
+/* }}} */
+/* {{{ proto mixed igbinary_serialize(string value) */
+PHP_FUNCTION(igbinary_serialize) {
+ (void) return_value_ptr;
+ (void) this_ptr;
+ (void) return_value_used;
+
+ zval *z;
+ struct igbinary_serialize_data igsd;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if (igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY TSRMLS_CC)) {
+ zend_error(E_WARNING, "igbinary_serialize: cannot init igsd");
+ RETURN_NULL();
+ }
+
+ igbinary_serialize_header(&igsd TSRMLS_CC);
+ igbinary_serialize_zval(&igsd, z TSRMLS_CC);
+
+ RETVAL_STRINGL((char *)igsd.buffer, igsd.buffer_size, 1);
+
+ igbinary_serialize_data_deinit(&igsd TSRMLS_CC);
+}
+/* }}} */
+/* {{{ Serializer encode function */
+PS_SERIALIZER_ENCODE_FUNC(igbinary)
+{
+ struct igbinary_serialize_data igsd;
+
+ if (igbinary_serialize_data_init(&igsd, false TSRMLS_CC)) {
+ zend_error(E_WARNING, "igbinary_serialize: cannot init igsd");
+ return FAILURE;
+ }
+
+ igbinary_serialize_header(&igsd TSRMLS_CC);
+ igbinary_serialize_array(&igsd, PS(http_session_vars), false, false TSRMLS_CC);
+
+ if (newlen)
+ *newlen = igsd.buffer_size;
+
+ *newstr = estrndup((char*)igsd.buffer, igsd.buffer_size);
+ if (newstr == NULL) {
+ return FAILURE;
+ }
+
+ igbinary_serialize_data_deinit(&igsd TSRMLS_CC);
+
+ return SUCCESS;
+}
+/* }}} */
+/* {{{ Serializer decode function */
+PS_SERIALIZER_DECODE_FUNC(igbinary) {
+ HashPosition tmp_hash_pos;
+ HashTable *tmp_hash;
+ char *key_str;
+ ulong key_long;
+ int tmp_int;
+ uint key_len;
+ zval *z;
+ zval **d;
+
+ struct igbinary_unserialize_data igsd;
+
+ if (!val || vallen==0)
+ return SUCCESS;
+
+ igbinary_unserialize_data_init(&igsd TSRMLS_CC);
+
+ igsd.buffer = (uint8_t *)val;
+ igsd.buffer_size = vallen;
+
+ if (igbinary_unserialize_header(&igsd TSRMLS_CC)) {
+ igbinary_unserialize_data_deinit(&igsd TSRMLS_CC);
+ return FAILURE;
+ }
+
+ ALLOC_INIT_ZVAL(z);
+ if (igbinary_unserialize_zval(&igsd, &z TSRMLS_CC)) {
+ igbinary_unserialize_data_deinit(&igsd TSRMLS_CC);
+ zval_dtor(z);
+ FREE_ZVAL(z);
+ return FAILURE;
+ }
+
+ igbinary_unserialize_data_deinit(&igsd TSRMLS_CC);
+
+ tmp_hash = HASH_OF(z);
+
+ zend_hash_internal_pointer_reset_ex(tmp_hash, &tmp_hash_pos);
+ while (zend_hash_get_current_data_ex(tmp_hash, (void *) &d, &tmp_hash_pos) == SUCCESS) {
+ tmp_int = zend_hash_get_current_key_ex(tmp_hash, &key_str, &key_len, &key_long, 0, &tmp_hash_pos);
+
+ switch (tmp_int) {
+ case HASH_KEY_IS_LONG:
+ /* ??? */
+ break;
+ case HASH_KEY_IS_STRING:
+ php_set_session_var(key_str, key_len-1, *d, NULL TSRMLS_CC);
+ php_add_session_var(key_str, key_len-1 TSRMLS_CC);
+ break;
+ }
+ zend_hash_move_forward_ex(tmp_hash, &tmp_hash_pos);
+ }
+ zval_dtor(z);
+ FREE_ZVAL(z);
+
+ return SUCCESS;
+}
+/* }}} */
+/* {{{ igbinary_serialize_data_init */
+/** Inits igbinary_serialize_data. */
+inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar TSRMLS_DC) {
+ int r = 0;
+
+ igsd->buffer = NULL;
+ igsd->buffer_size = 0;
+ igsd->buffer_capacity = 32;
+ igsd->error = 0;
+
+ igsd->buffer = (uint8_t *) emalloc(igsd->buffer_capacity);
+ if (igsd->buffer == NULL) {
+ return 1;
+ }
+
+ igsd->scalar = scalar;
+ if (!igsd->scalar) {
+ hash_si_init(&igsd->strings, 16);
+ hash_si_init(&igsd->objects, 16);
+ }
+
+ igsd->compact_strings = (bool)IGBINARY_G(compact_strings);
+
+ return r;
+}
+/* }}} */
+/* {{{ igbinary_serialize_data_deinit */
+/** Deinits igbinary_serialize_data. */
+inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd TSRMLS_DC) {
+ if (igsd->buffer) {
+ efree(igsd->buffer);
+ }
+
+ if (!igsd->scalar) {
+ hash_si_deinit(&igsd->strings);
+ hash_si_deinit(&igsd->objects);
+ }
+}
+/* }}} */
+/* {{{ igbinary_serialize_header */
+/** Serializes header. */
+inline static int igbinary_serialize_header(struct igbinary_serialize_data *igsd TSRMLS_DC) {
+ igbinary_serialize32(igsd, IGBINARY_FORMAT_VERSION TSRMLS_CC); /* version */
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_resize */
+/** Expandes igbinary_serialize_data. */
+inline static int igbinary_serialize_resize(struct igbinary_serialize_data *igsd, size_t size TSRMLS_DC) {
+ if (igsd->buffer_size + size < igsd->buffer_capacity) {
+ return 0;
+ }
+
+ while (igsd->buffer_size + size >= igsd->buffer_capacity) {
+ igsd->buffer_capacity *= 2;
+ }
+
+ igsd->buffer = (uint8_t *) erealloc(igsd->buffer, igsd->buffer_capacity);
+ if (igsd->buffer == NULL)
+ return 1;
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize8 */
+/** Serialize 8bit value. */
+inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i TSRMLS_DC) {
+ if (igbinary_serialize_resize(igsd, 1 TSRMLS_CC)) {
+ return 1;
+ }
+
+ igsd->buffer[igsd->buffer_size++] = i;
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize16 */
+/** Serialize 16bit value. */
+inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i TSRMLS_DC) {
+ if (igbinary_serialize_resize(igsd, 2 TSRMLS_CC)) {
+ return 1;
+ }
+
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 8 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i & 0xff);
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize32 */
+/** Serialize 32bit value. */
+inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i TSRMLS_DC) {
+ if (igbinary_serialize_resize(igsd, 4 TSRMLS_CC)) {
+ return 1;
+ }
+
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 24 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 16 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 8 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i & 0xff);
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize64 */
+/** Serialize 64bit value. */
+inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i TSRMLS_DC) {
+ if (igbinary_serialize_resize(igsd, 8 TSRMLS_CC)) {
+ return 1;
+ }
+
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 56 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 48 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 40 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 32 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 24 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 16 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i >> 8 & 0xff);
+ igsd->buffer[igsd->buffer_size++] = (uint8_t) (i & 0xff);
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_null */
+/** Serializes null. */
+inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd TSRMLS_DC) {
+ return igbinary_serialize8(igsd, igbinary_type_null TSRMLS_CC);
+}
+/* }}} */
+/* {{{ igbinary_serialize_bool */
+/** Serializes bool. */
+inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b TSRMLS_DC) {
+ return igbinary_serialize8(igsd, (uint8_t) (b ? igbinary_type_bool_true : igbinary_type_bool_false) TSRMLS_CC);
+}
+/* }}} */
+/* {{{ igbinary_serialize_long */
+/** Serializes long. */
+inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, long l TSRMLS_DC) {
+ long k = l >= 0 ? l : -l;
+ bool p = l >= 0 ? true : false;
+
+ /* -LONG_MIN is 0 otherwise. */
+ if (l == LONG_MIN) {
+#if SIZEOF_LONG == 8
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_long64n TSRMLS_CC);
+ igbinary_serialize64(igsd, (uint64_t) 0x8000000000000000 TSRMLS_CC);
+#elif SIZEOF_LONG == 4
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_long32n TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) 0x80000000 TSRMLS_CC);
+#else
+#error "Strange sizeof(long)."
+#endif
+ return 0;
+ }
+
+ if (k <= 0xff) {
+ igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long8p : igbinary_type_long8n) TSRMLS_CC);
+ igbinary_serialize8(igsd, (uint8_t) k TSRMLS_CC);
+ } else if (k <= 0xffff) {
+ igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long16p : igbinary_type_long16n) TSRMLS_CC);
+ igbinary_serialize16(igsd, (uint16_t) k TSRMLS_CC);
+#if SIZEOF_LONG == 8
+ } else if (k <= 0xffffffff) {
+ igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n) TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) k TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long64p : igbinary_type_long64n) TSRMLS_CC);
+ igbinary_serialize64(igsd, (uint64_t) k TSRMLS_CC);
+ }
+#elif SIZEOF_LONG == 4
+ } else {
+ igbinary_serialize8(igsd, (uint8_t) (p ? igbinary_type_long32p : igbinary_type_long32n) TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) k TSRMLS_CC);
+ }
+#else
+#error "Strange sizeof(long)."
+#endif
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_double */
+/** Serializes double. */
+inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d TSRMLS_DC) {
+ igbinary_serialize8(igsd, igbinary_type_double TSRMLS_CC);
+
+ union {
+ double d;
+ uint64_t u;
+ } u;
+
+ u.d = d;
+
+ igbinary_serialize64(igsd, u.u TSRMLS_CC);
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_string */
+/** Serializes string.
+ * Serializes each string once, after first time uses pointers.
+ */
+inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, char *s, size_t len TSRMLS_DC) {
+ uint32_t t;
+ uint32_t *i = &t;
+
+ if (len == 0) {
+ igbinary_serialize8(igsd, igbinary_type_string_empty TSRMLS_CC);
+ return 0;
+ }
+
+ if (igsd->scalar || !igsd->compact_strings || hash_si_find(&igsd->strings, s, len, i) == 1) {
+ if (!igsd->scalar && igsd->compact_strings) {
+ t = hash_si_size(&igsd->strings);
+ hash_si_insert(&igsd->strings, s, len, t);
+ }
+
+ igbinary_serialize_chararray(igsd, s, len TSRMLS_CC);
+ } else {
+ if (*i <= 0xff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id8 TSRMLS_CC);
+ igbinary_serialize8(igsd, (uint8_t) *i TSRMLS_CC);
+ } else if (*i <= 0xffff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id16 TSRMLS_CC);
+ igbinary_serialize16(igsd, (uint16_t) *i TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_string_id32 TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) *i TSRMLS_CC);
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_chararray */
+/** Serializes string data. */
+inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len TSRMLS_DC) {
+ if (len <= 0xff) {
+ igbinary_serialize8(igsd, igbinary_type_string8 TSRMLS_CC);
+ igbinary_serialize8(igsd, len TSRMLS_CC);
+ } else if (len <= 0xffff) {
+ igbinary_serialize8(igsd, igbinary_type_string16 TSRMLS_CC);
+ igbinary_serialize16(igsd, len TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, igbinary_type_string32 TSRMLS_CC);
+ igbinary_serialize32(igsd, len TSRMLS_CC);
+ }
+
+ if (igbinary_serialize_resize(igsd, len TSRMLS_CC)) {
+ return 1;
+ }
+
+ memcpy(igsd->buffer+igsd->buffer_size, s, len);
+ igsd->buffer_size += len;
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinay_serialize_array */
+/** Serializes array or objects inner properties. */
+inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class TSRMLS_DC) {
+ HashTable *h;
+ HashPosition pos;
+ size_t n;
+ zval **d;
+
+ char *key;
+ uint key_len;
+ int key_type;
+ ulong key_index;
+
+ /* hash */
+ h = object ? Z_OBJPROP_P(z) : HASH_OF(z);
+
+ /* hash size */
+ n = h ? zend_hash_num_elements(h) : 0;
+
+ /* incomplete class magic member */
+ if (n > 0 && incomplete_class) {
+ --n;
+ }
+
+ if (!object && igbinary_serialize_array_ref(igsd, z, object TSRMLS_CC) == 0) {
+ return 0;
+ }
+
+ if (n <= 0xff) {
+ igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC);
+ igbinary_serialize8(igsd, n TSRMLS_CC);
+ } else if (n <= 0xffff) {
+ igbinary_serialize8(igsd, igbinary_type_array16 TSRMLS_CC);
+ igbinary_serialize16(igsd, n TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, igbinary_type_array32 TSRMLS_CC);
+ igbinary_serialize32(igsd, n TSRMLS_CC);
+ }
+
+ if (n == 0) {
+ return 0;
+ }
+
+ /* serialize properties. */
+ zend_hash_internal_pointer_reset_ex(h, &pos);
+ for (;; zend_hash_move_forward_ex(h, &pos)) {
+ key_type = zend_hash_get_current_key_ex(h, &key, &key_len, &key_index, 0, &pos);
+
+ /* last */
+ if (key_type == HASH_KEY_NON_EXISTANT) {
+ break;
+ }
+
+ /* skip magic member in incomplete classes */
+ if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) {
+ continue;
+ }
+
+ switch (key_type) {
+ case HASH_KEY_IS_LONG:
+ igbinary_serialize_long(igsd, key_index TSRMLS_CC);
+ break;
+ case HASH_KEY_IS_STRING:
+
+ igbinary_serialize_string(igsd, key, key_len-1 TSRMLS_CC);
+ break;
+ default:
+ zend_error(E_ERROR, "igbinary_serialize_array: key is not string nor array");
+ /* not reached */
+ return 1;
+ }
+
+ /* we should still add element even if it's not OK,
+ * since we already wrote the length of the array before */
+ if (zend_hash_get_current_data_ex(h, (void *) &d, &pos) != SUCCESS || d == NULL) {
+ if (igbinary_serialize_null(igsd TSRMLS_CC)) {
+ return 1;
+ }
+ } else {
+ if (igbinary_serialize_zval(igsd, *d TSRMLS_CC)) {
+ return 1;
+ }
+ }
+
+ }
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_array_ref */
+/** Serializes array reference. */
+inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object TSRMLS_DC) {
+ uint32_t t = 0;
+ uint32_t *i = &t;
+ union {
+ zval *z;
+ struct {
+ zend_class_entry *ce;
+ zend_object_handle handle;
+ } obj;
+ } key = { 0 };
+
+ if (object && Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get_class_entry) {
+ key.obj.ce = Z_OBJCE_P(z);
+ key.obj.handle = Z_OBJ_HANDLE_P(z);
+ } else {
+ key.z = z;
+ }
+
+ if (hash_si_find(&igsd->objects, (char *)&key, sizeof(key), i) == 1) {
+ t = hash_si_size(&igsd->objects);
+ hash_si_insert(&igsd->objects, (char *)&key, sizeof(key), t);
+ return 1;
+ } else {
+ enum igbinary_type type;
+ if (*i <= 0xff) {
+ type = object ? igbinary_type_objref8 : igbinary_type_ref8;
+ igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC);
+ igbinary_serialize8(igsd, (uint8_t) *i TSRMLS_CC);
+ } else if (*i <= 0xffff) {
+ type = object ? igbinary_type_objref16 : igbinary_type_ref16;
+ igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC);
+ igbinary_serialize16(igsd, (uint16_t) *i TSRMLS_CC);
+ } else {
+ type = object ? igbinary_type_objref32 : igbinary_type_ref32;
+ igbinary_serialize8(igsd, (uint8_t) type TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) *i TSRMLS_CC);
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+/* {{{ igbinary_serialize_array_sleep */
+/** Serializes object's properties array with __sleep -function. */
+inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, zend_class_entry *ce, bool incomplete_class TSRMLS_DC) {
+ HashPosition pos;
+ size_t n = zend_hash_num_elements(h);
+ zval **d;
+ zval **v;
+
+ char *key;
+ uint key_len;
+ int key_type;
+ ulong key_index;
+
+ /* Decrease array size by one, because of magic member (with class name) */
+ if (n > 0 && incomplete_class) {
+ --n;
+ }
+
+ /* Serialize array id. */
+ if (n <= 0xff) {
+ igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC);
+ igbinary_serialize8(igsd, n TSRMLS_CC);
+ } else if (n <= 0xffff) {
+ igbinary_serialize8(igsd, igbinary_type_array16 TSRMLS_CC);
+ igbinary_serialize16(igsd, n TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, igbinary_type_array32 TSRMLS_CC);
+ igbinary_serialize32(igsd, n TSRMLS_CC);
+ }
+
+ if (n == 0) {
+ return 0;
+ }
+
+ zend_hash_internal_pointer_reset_ex(h, &pos);
+
+ for (;; zend_hash_move_forward_ex(h, &pos)) {
+ key_type = zend_hash_get_current_key_ex(h, &key, &key_len, &key_index, 0, &pos);
+
+ /* last */
+ if (key_type == HASH_KEY_NON_EXISTANT) {
+ break;
+ }
+
+ /* skip magic member in incomplete classes */
+ if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) {
+ continue;
+ }
+
+ if (zend_hash_get_current_data_ex(h, (void *) &d, &pos) != SUCCESS || d == NULL || Z_TYPE_PP(d) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only "
+ "containing the names of instance-variables to "
+ "serialize");
+
+ /* we should still add element even if it's not OK,
+ * since we already wrote the length of the array before
+ * serialize null as key-value pair */
+ igbinary_serialize_null(igsd TSRMLS_CC);
+ } else {
+
+ if (zend_hash_find(Z_OBJPROP_P(z), Z_STRVAL_PP(d), Z_STRLEN_PP(d) + 1, (void *) &v) == SUCCESS) {
+ igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC);
+ igbinary_serialize_zval(igsd, *v TSRMLS_CC);
+ } else if (ce) {
+ char *prot_name = NULL;
+ char *priv_name = NULL;
+ int prop_name_length;
+
+ do {
+ /* try private */
+ zend_mangle_property_name(&priv_name, &prop_name_length, ce->name, ce->name_length,
+ Z_STRVAL_PP(d), Z_STRLEN_PP(d), ce->type & ZEND_INTERNAL_CLASS);
+ if (zend_hash_find(Z_OBJPROP_P(z), priv_name, prop_name_length+1, (void *) &v) == SUCCESS) {
+ igbinary_serialize_string(igsd, priv_name, prop_name_length TSRMLS_CC);
+ efree(priv_name);
+ igbinary_serialize_zval(igsd, *v TSRMLS_CC);
+ break;
+ }
+ efree(priv_name);
+
+ /* try protected */
+ zend_mangle_property_name(&prot_name, &prop_name_length, "*", 1,
+ Z_STRVAL_PP(d), Z_STRLEN_PP(d), ce->type & ZEND_INTERNAL_CLASS);
+ if (zend_hash_find(Z_OBJPROP_P(z), prot_name, prop_name_length+1, (void *) &v) == SUCCESS) {
+ igbinary_serialize_string(igsd, prot_name, prop_name_length TSRMLS_CC);
+ efree(prot_name);
+ igbinary_serialize_zval(igsd, *v TSRMLS_CC);
+ break;
+ }
+ efree(prot_name);
+
+ /* no win */
+ igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC);
+ igbinary_serialize_null(igsd TSRMLS_CC);
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", Z_STRVAL_PP(d));
+ } while (0);
+
+ } else {
+ // if all else fails, just serialize the value in anyway.
+ igbinary_serialize_string(igsd, Z_STRVAL_PP(d), Z_STRLEN_PP(d) TSRMLS_CC);
+ igbinary_serialize_zval(igsd, *v TSRMLS_CC);
+ }
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_object_name */
+/** Serialize object name. */
+inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, const char *class_name, size_t name_len TSRMLS_DC) {
+ uint32_t t;
+ uint32_t *i = &t;
+
+ if (hash_si_find(&igsd->strings, class_name, name_len, i) == 1) {
+ t = hash_si_size(&igsd->strings);
+ hash_si_insert(&igsd->strings, class_name, name_len, t);
+
+ if (name_len <= 0xff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object8 TSRMLS_CC);
+ igbinary_serialize8(igsd, (uint8_t) name_len TSRMLS_CC);
+ } else if (name_len <= 0xffff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object16 TSRMLS_CC);
+ igbinary_serialize16(igsd, (uint16_t) name_len TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object32 TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) name_len TSRMLS_CC);
+ }
+
+ if (igbinary_serialize_resize(igsd, name_len TSRMLS_CC)) {
+ return 1;
+ }
+
+ memcpy(igsd->buffer+igsd->buffer_size, class_name, name_len);
+ igsd->buffer_size += name_len;
+ } else {
+ /* already serialized string */
+ if (*i <= 0xff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id8 TSRMLS_CC);
+ igbinary_serialize8(igsd, (uint8_t) *i TSRMLS_CC);
+ } else if (*i <= 0xffff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id16 TSRMLS_CC);
+ igbinary_serialize16(igsd, (uint16_t) *i TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_id32 TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) *i TSRMLS_CC);
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_serialize_object */
+/** Serialize object.
+ * @see ext/standard/var.c
+ * */
+inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC) {
+ zend_class_entry *ce;
+
+ zval f;
+ zval *h = NULL;
+
+ int r = 0;
+
+ unsigned char *serialized_data = NULL;
+ zend_uint serialized_len;
+
+ PHP_CLASS_ATTRIBUTES;
+
+ if (igbinary_serialize_array_ref(igsd, z, true TSRMLS_CC) == 0) {
+ return r;
+ }
+
+ ce = Z_OBJCE_P(z);
+
+ /* custom serializer */
+ if (ce && ce->serialize != NULL) {
+ /* TODO: var_hash? */
+ if(ce->serialize(z, &serialized_data, &serialized_len, (zend_serialize_data *)NULL TSRMLS_CC) == SUCCESS && !EG(exception)) {
+ igbinary_serialize_object_name(igsd, ce->name, ce->name_length TSRMLS_CC);
+
+ if (serialized_len <= 0xff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser8 TSRMLS_CC);
+ igbinary_serialize8(igsd, (uint8_t) serialized_len TSRMLS_CC);
+ } else if (serialized_len <= 0xffff) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser16 TSRMLS_CC);
+ igbinary_serialize16(igsd, (uint16_t) serialized_len TSRMLS_CC);
+ } else {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_object_ser32 TSRMLS_CC);
+ igbinary_serialize32(igsd, (uint32_t) serialized_len TSRMLS_CC);
+ }
+
+ if (igbinary_serialize_resize(igsd, serialized_len TSRMLS_CC)) {
+ if (serialized_data) {
+ efree(serialized_data);
+ }
+ r = 1;
+
+ return r;
+ }
+
+ memcpy(igsd->buffer+igsd->buffer_size, serialized_data, serialized_len);
+ igsd->buffer_size += serialized_len;
+ } else if (EG(exception)) {
+ /* exception, return failure */
+ r = 1;
+ } else {
+ /* Serialization callback failed, assume null output */
+ igbinary_serialize_null(igsd TSRMLS_CC);
+ }
+
+ if (serialized_data) {
+ efree(serialized_data);
+ }
+
+ return r;
+ }
+
+ /* serialize class name */
+ PHP_SET_CLASS_ATTRIBUTES(z);
+ igbinary_serialize_object_name(igsd, class_name, name_len TSRMLS_CC);
+ PHP_CLEANUP_CLASS_ATTRIBUTES();
+
+ if (ce && ce != PHP_IC_ENTRY && zend_hash_exists(&ce->function_table, "__sleep", sizeof("__sleep"))) {
+ /* function name string */
+ INIT_PZVAL(&f);
+ ZVAL_STRINGL(&f, "__sleep", sizeof("__sleep") - 1, 0);
+
+ /* calling z->__sleep */
+ r = call_user_function_ex(CG(function_table), &z, &f, &h, 0, 0, 1, NULL TSRMLS_CC);
+
+ if (r == SUCCESS && !EG(exception)) {
+ r = 0;
+
+ if (h) {
+ if (Z_TYPE_P(h) == IS_ARRAY) {
+ r = igbinary_serialize_array_sleep(igsd, z, HASH_OF(h), ce, incomplete_class TSRMLS_CC);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only "
+ "containing the names of instance-variables to "
+ "serialize");
+
+ /* empty array */
+ igbinary_serialize8(igsd, igbinary_type_array8 TSRMLS_CC);
+ r = igbinary_serialize8(igsd, 0 TSRMLS_CC);
+ }
+ }
+ } else {
+ r = 1;
+ }
+
+ /* cleanup */
+ if (h) {
+ zval_ptr_dtor(&h);
+ }
+
+ return r;
+ } else {
+ return igbinary_serialize_array(igsd, z, true, incomplete_class TSRMLS_CC);
+ }
+}
+/* }}} */
+/* {{{ igbinary_serialize_zval */
+/** Serialize zval. */
+static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z TSRMLS_DC) {
+ if (Z_ISREF_P(z)) {
+ igbinary_serialize8(igsd, (uint8_t) igbinary_type_ref TSRMLS_CC);
+ }
+ switch (Z_TYPE_P(z)) {
+ case IS_RESOURCE:
+ return igbinary_serialize_null(igsd TSRMLS_CC);
+ case IS_OBJECT:
+ return igbinary_serialize_object(igsd, z TSRMLS_CC);
+ case IS_ARRAY:
+ return igbinary_serialize_array(igsd, z, false, false TSRMLS_CC);
+ case IS_STRING:
+ return igbinary_serialize_string(igsd, Z_STRVAL_P(z), Z_STRLEN_P(z) TSRMLS_CC);
+ case IS_LONG:
+ return igbinary_serialize_long(igsd, Z_LVAL_P(z) TSRMLS_CC);
+ case IS_NULL:
+ return igbinary_serialize_null(igsd TSRMLS_CC);
+ case IS_BOOL:
+ return igbinary_serialize_bool(igsd, Z_LVAL_P(z) ? 1 : 0 TSRMLS_CC);
+ case IS_DOUBLE:
+ return igbinary_serialize_double(igsd, Z_DVAL_P(z) TSRMLS_CC);
+ default:
+ zend_error(E_ERROR, "igbinary_serialize_zval: zval has unknown type %d", (int)Z_TYPE_P(z));
+ /* not reached */
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+/* {{{ igbinary_unserialize_data_init */
+/** Inits igbinary_unserialize_data_init. */
+inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd TSRMLS_DC) {
+ smart_str empty_str = { 0 };
+
+ igsd->buffer = NULL;
+ igsd->buffer_size = 0;
+ igsd->buffer_offset = 0;
+
+ igsd->strings = NULL;
+ igsd->strings_count = 0;
+ igsd->strings_capacity = 4;
+ igsd->string0_buf = empty_str;
+
+ igsd->error = 0;
+ igsd->references = NULL;
+ igsd->references_count = 0;
+ igsd->references_capacity = 4;
+
+ igsd->references = (void **) emalloc(sizeof(void *) * igsd->references_capacity);
+ if (igsd->references == NULL) {
+ return 1;
+ }
+
+ igsd->strings = (struct igbinary_unserialize_string_pair *) emalloc(sizeof(struct igbinary_unserialize_string_pair) * igsd->strings_capacity);
+ if (igsd->strings == NULL) {
+ efree(igsd->references);