summaryrefslogtreecommitdiff
path: root/vendor/wikimedia/avro
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/wikimedia/avro')
-rw-r--r--vendor/wikimedia/avro/LICENSE.txt308
-rw-r--r--vendor/wikimedia/avro/NOTICE.txt9
-rw-r--r--vendor/wikimedia/avro/README.md23
-rw-r--r--vendor/wikimedia/avro/lib/avro.php195
-rw-r--r--vendor/wikimedia/avro/lib/avro/data_file.php535
-rw-r--r--vendor/wikimedia/avro/lib/avro/datum.php984
-rw-r--r--vendor/wikimedia/avro/lib/avro/debug.php194
-rw-r--r--vendor/wikimedia/avro/lib/avro/gmp.php222
-rw-r--r--vendor/wikimedia/avro/lib/avro/io.php494
-rw-r--r--vendor/wikimedia/avro/lib/avro/protocol.php86
-rw-r--r--vendor/wikimedia/avro/lib/avro/schema.php1457
-rw-r--r--vendor/wikimedia/avro/lib/avro/util.php67
12 files changed, 4574 insertions, 0 deletions
diff --git a/vendor/wikimedia/avro/LICENSE.txt b/vendor/wikimedia/avro/LICENSE.txt
new file mode 100644
index 00000000..6d3f211b
--- /dev/null
+++ b/vendor/wikimedia/avro/LICENSE.txt
@@ -0,0 +1,308 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+----------------------------------------------------------------------
+License for the Jansson C JSON parser used in the C implementation:
+
+Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+----------------------------------------------------------------------
+License for the Json.NET used in the C# implementation:
+
+Copyright (c) 2007 James Newton-King
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+----------------------------------------------------------------------
+License for msinttypes used in the C implementation:
+Source from:
+http://code.google.com/p/msinttypes/downloads/detail?name=msinttypes-r26.zip
+
+Copyright (c) 2006-2008 Alexander Chemeris
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+----------------------------------------------------------------------
+License for Dirent API for Microsoft Visual Studio used in the C implementation:
+Source from:
+http://www.softagalleria.net/download/dirent/dirent-1.11.zip
+
+Copyright (C) 2006 Toni Ronkko
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+``Software''), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+----------------------------------------------------------------------
diff --git a/vendor/wikimedia/avro/NOTICE.txt b/vendor/wikimedia/avro/NOTICE.txt
new file mode 100644
index 00000000..e601a8e9
--- /dev/null
+++ b/vendor/wikimedia/avro/NOTICE.txt
@@ -0,0 +1,9 @@
+Apache Avro
+Copyright 2010 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+C JSON parsing provided by Jansson and
+written by Petri Lehtinen. The original software is
+available from http://www.digip.org/jansson/.
diff --git a/vendor/wikimedia/avro/README.md b/vendor/wikimedia/avro/README.md
new file mode 100644
index 00000000..5604baf2
--- /dev/null
+++ b/vendor/wikimedia/avro/README.md
@@ -0,0 +1,23 @@
+What the Avro PHP library is
+============================
+
+A library for using [Avro](http://avro.apache.org/) with PHP.
+
+Requirements
+------------
+ * PHP 5
+ * On 32-bit platforms, the [GMP PHP extension](http://php.net/gmp)
+ * For testing, [PHPUnit](http://www.phpunit.de/)
+
+Both GMP and PHPUnit are often available via package management
+systems as `php5-gmp` and `phpunit`, respectively.
+
+Getting started
+---------------
+```
+$ composer require wikimedia/avro
+```
+
+History
+-------
+Extracted from https://github.com/apache/avro using `git subtree`.
diff --git a/vendor/wikimedia/avro/lib/avro.php b/vendor/wikimedia/avro/lib/avro.php
new file mode 100644
index 00000000..4805fb7a
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Avro library top-level file.
+ *
+ * This file in turn includes all files supporting the
+ * Avro implementation.
+ *
+ * @package Avro
+ */
+
+/**
+ * General Avro exceptions.
+ * @package Avro
+ */
+class AvroException extends Exception {}
+
+/**
+ * Library-level class for PHP Avro port.
+ *
+ * Contains library details such as version number and platform checks.
+ *
+ * This port is an implementation of the
+ * {@link http://avro.apache.org/docs/1.3.3/spec.html Avro 1.3.3 Specification}
+ *
+ * @package Avro
+ *
+ */
+class Avro
+{
+ /**
+ * @var string version number of Avro specification to which
+ * this implemenation complies
+ */
+ const SPEC_VERSION = '1.3.3';
+
+ /**#@+
+ * Constant to enumerate endianness.
+ * @access private
+ * @var int
+ */
+ const BIG_ENDIAN = 0x00;
+ const LITTLE_ENDIAN = 0x01;
+ /**#@-*/
+
+ /**
+ * Memoized result of self::set_endianness()
+ * @var int self::BIG_ENDIAN or self::LITTLE_ENDIAN
+ * @see self::set_endianness()
+ */
+ private static $endianness;
+
+ /**#@+
+ * Constant to enumerate biginteger handling mode.
+ * GMP is used, if available, on 32-bit platforms.
+ */
+ const PHP_BIGINTEGER_MODE = 0x00;
+ const GMP_BIGINTEGER_MODE = 0x01;
+ /**#@-*/
+
+ /**
+ * @var int
+ * Mode used to handle bigintegers. After Avro::check_64_bit() has been called,
+ * (usually via a call to Avro::check_platform(), set to
+ * self::GMP_BIGINTEGER_MODE on 32-bit platforms that have GMP available,
+ * and to self::PHP_BIGINTEGER_MODE otherwise.
+ */
+ private static $biginteger_mode;
+
+ /**
+ * Wrapper method to call each required check.
+ *
+ */
+ public static function check_platform()
+ {
+ self::check_64_bit();
+ self::check_little_endian();
+ }
+
+ /**
+ * Determines if the host platform can encode and decode long integer data.
+ *
+ * @throws AvroException if the platform cannot handle long integers.
+ */
+ private static function check_64_bit()
+ {
+ if (8 != PHP_INT_SIZE)
+ if (extension_loaded('gmp'))
+ self::$biginteger_mode = self::GMP_BIGINTEGER_MODE;
+ else
+ throw new AvroException('This platform cannot handle a 64-bit operations. '
+ . 'Please install the GMP PHP extension.');
+ else
+ self::$biginteger_mode = self::PHP_BIGINTEGER_MODE;
+
+ }
+
+ /**
+ * @returns boolean true if the PHP GMP extension is used and false otherwise.
+ * @internal Requires Avro::check_64_bit() (exposed via Avro::check_platform())
+ * to have been called to set Avro::$biginteger_mode.
+ */
+ static function uses_gmp()
+ {
+ return (self::GMP_BIGINTEGER_MODE == self::$biginteger_mode);
+ }
+
+ /**
+ * Determines if the host platform is little endian,
+ * required for processing double and float data.
+ *
+ * @throws AvroException if the platform is not little endian.
+ */
+ private static function check_little_endian()
+ {
+ if (!self::is_little_endian_platform())
+ throw new AvroException('This is not a little-endian platform');
+ }
+
+ /**
+ * Determines the endianness of the host platform and memoizes
+ * the result to Avro::$endianness.
+ *
+ * Based on a similar check perfomed in http://pear.php.net/package/Math_BinaryUtils
+ *
+ * @throws AvroException if the endianness cannot be determined.
+ */
+ private static function set_endianness()
+ {
+ $packed = pack('d', 1);
+ switch ($packed)
+ {
+ case "\77\360\0\0\0\0\0\0":
+ self::$endianness = self::BIG_ENDIAN;
+ break;
+ case "\0\0\0\0\0\0\360\77":
+ self::$endianness = self::LITTLE_ENDIAN;
+ break;
+ default:
+ throw new AvroException(
+ sprintf('Error determining platform endianness: %s',
+ AvroDebug::hex_string($packed)));
+ }
+ }
+
+ /**
+ * @returns boolean true if the host platform is big endian
+ * and false otherwise.
+ * @uses self::set_endianness()
+ */
+ private static function is_big_endian_platform()
+ {
+ if (is_null(self::$endianness))
+ self::set_endianness();
+
+ return (self::BIG_ENDIAN == self::$endianness);
+ }
+
+ /**
+ * @returns boolean true if the host platform is little endian,
+ * and false otherwise.
+ * @uses self::is_bin_endian_platform()
+ */
+ private static function is_little_endian_platform()
+ {
+ return !self::is_big_endian_platform();
+ }
+
+}
+
+require_once('avro/util.php');
+require_once('avro/debug.php');
+require_once('avro/schema.php');
+require_once('avro/io.php');
+require_once('avro/gmp.php');
+require_once('avro/datum.php');
+require_once('avro/data_file.php');
+require_once('avro/protocol.php');
diff --git a/vendor/wikimedia/avro/lib/avro/data_file.php b/vendor/wikimedia/avro/lib/avro/data_file.php
new file mode 100644
index 00000000..e8e089f5
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/data_file.php
@@ -0,0 +1,535 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes handling reading and writing from and to AvroIO objects
+ * @package Avro
+ */
+
+/**
+ * Raised when something unkind happens with respect to AvroDataIO.
+ * @package Avro
+ */
+class AvroDataIOException extends AvroException {}
+
+/**
+ * @package Avro
+ */
+class AvroDataIO
+{
+ /**
+ * @var int used in file header
+ */
+ const VERSION = 1;
+
+ /**
+ * @var int count of bytes in synchronization marker
+ */
+ const SYNC_SIZE = 16;
+
+ /**
+ * @var int count of items per block, arbitrarily set to 4000 * SYNC_SIZE
+ * @todo make this value configurable
+ */
+ const SYNC_INTERVAL = 64000;
+
+ /**
+ * @var string map key for datafile metadata codec value
+ */
+ const METADATA_CODEC_ATTR = 'avro.codec';
+
+ /**
+ * @var string map key for datafile metadata schema value
+ */
+ const METADATA_SCHEMA_ATTR = 'avro.schema';
+ /**
+ * @var string JSON for datafile metadata schema
+ */
+ const METADATA_SCHEMA_JSON = '{"type":"map","values":"bytes"}';
+
+ /**
+ * @var string codec value for NULL codec
+ */
+ const NULL_CODEC = 'null';
+
+ /**
+ * @var string codec value for deflate codec
+ */
+ const DEFLATE_CODEC = 'deflate';
+
+ /**
+ * @var array array of valid codec names
+ * @todo Avro implementations are required to implement deflate codec as well,
+ * so implement it already!
+ */
+ private static $valid_codecs = array(self::NULL_CODEC);
+
+ /**
+ * @var AvroSchema cached version of metadata schema object
+ */
+ private static $metadata_schema;
+
+ /**
+ * @returns the initial "magic" segment of an Avro container file header.
+ */
+ public static function magic() { return ('Obj' . pack('c', self::VERSION)); }
+
+ /**
+ * @returns int count of bytes in the initial "magic" segment of the
+ * Avro container file header
+ */
+ public static function magic_size() { return strlen(self::magic()); }
+
+
+ /**
+ * @returns AvroSchema object of Avro container file metadata.
+ */
+ public static function metadata_schema()
+ {
+ if (is_null(self::$metadata_schema))
+ self::$metadata_schema = AvroSchema::parse(self::METADATA_SCHEMA_JSON);
+ return self::$metadata_schema;
+ }
+
+ /**
+ * @param string $file_path file_path of file to open
+ * @param string $mode one of AvroFile::READ_MODE or AvroFile::WRITE_MODE
+ * @param string $schema_json JSON of writer's schema
+ * @returns AvroDataIOWriter instance of AvroDataIOWriter
+ *
+ * @throws AvroDataIOException if $writers_schema is not provided
+ * or if an invalid $mode is given.
+ */
+ public static function open_file($file_path, $mode=AvroFile::READ_MODE,
+ $schema_json=null)
+ {
+ $schema = !is_null($schema_json)
+ ? AvroSchema::parse($schema_json) : null;
+
+ $io = false;
+ switch ($mode)
+ {
+ case AvroFile::WRITE_MODE:
+ if (is_null($schema))
+ throw new AvroDataIOException('Writing an Avro file requires a schema.');
+ $file = new AvroFile($file_path, AvroFile::WRITE_MODE);
+ $io = self::open_writer($file, $schema);
+ break;
+ case AvroFile::READ_MODE:
+ $file = new AvroFile($file_path, AvroFile::READ_MODE);
+ $io = self::open_reader($file, $schema);
+ break;
+ default:
+ throw new AvroDataIOException(
+ sprintf("Only modes '%s' and '%s' allowed. You gave '%s'.",
+ AvroFile::READ_MODE, AvroFile::WRITE_MODE, $mode));
+ }
+ return $io;
+ }
+
+ /**
+ * @returns array array of valid codecs
+ */
+ private static function valid_codecs()
+ {
+ return self::$valid_codecs;
+ }
+
+ /**
+ * @param string $codec
+ * @returns boolean true if $codec is a valid codec value and false otherwise
+ */
+ public static function is_valid_codec($codec)
+ {
+ return in_array($codec, self::valid_codecs());
+ }
+
+ /**
+ * @param AvroIO $io
+ * @param AvroSchema $schema
+ * @returns AvroDataIOWriter
+ */
+ protected function open_writer($io, $schema)
+ {
+ $writer = new AvroIODatumWriter($schema);
+ return new AvroDataIOWriter($io, $writer, $schema);
+ }
+
+ /**
+ * @param AvroIO $io
+ * @param AvroSchema $schema
+ * @returns AvroDataIOReader
+ */
+ protected function open_reader($io, $schema)
+ {
+ $reader = new AvroIODatumReader(null, $schema);
+ return new AvroDataIOReader($io, $reader);
+ }
+
+}
+
+/**
+ *
+ * Reads Avro data from an AvroIO source using an AvroSchema.
+ * @package Avro
+ */
+class AvroDataIOReader
+{
+ /**
+ * @var AvroIO
+ */
+ private $io;
+
+ /**
+ * @var AvroIOBinaryDecoder
+ */
+ private $decoder;
+
+ /**
+ * @var AvroIODatumReader
+ */
+ private $datum_reader;
+
+ /**
+ * @var string
+ */
+ private $sync_marker;
+
+ /**
+ * @var array object container metadata
+ */
+ private $metadata;
+
+ /**
+ * @var int count of items in block
+ */
+ private $block_count;
+
+ /**
+ * @param AvroIO $io source from which to read
+ * @param AvroIODatumReader $datum_reader reader that understands
+ * the data schema
+ * @throws AvroDataIOException if $io is not an instance of AvroIO
+ * @uses read_header()
+ */
+ public function __construct($io, $datum_reader)
+ {
+
+ if (!($io instanceof AvroIO))
+ throw new AvroDataIOException('io must be instance of AvroIO');
+
+ $this->io = $io;
+ $this->decoder = new AvroIOBinaryDecoder($this->io);
+ $this->datum_reader = $datum_reader;
+ $this->read_header();
+
+ $codec = AvroUtil::array_value($this->metadata,
+ AvroDataIO::METADATA_CODEC_ATTR);
+ if ($codec && !AvroDataIO::is_valid_codec($codec))
+ throw new AvroDataIOException(sprintf('Uknown codec: %s', $codec));
+
+ $this->block_count = 0;
+ // FIXME: Seems unsanitary to set writers_schema here.
+ // Can't constructor take it as an argument?
+ $this->datum_reader->set_writers_schema(
+ AvroSchema::parse($this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR]));
+ }
+
+ /**
+ * Reads header of object container
+ * @throws AvroDataIOException if the file is not an Avro data file.
+ */
+ private function read_header()
+ {
+ $this->seek(0, AvroIO::SEEK_SET);
+
+ $magic = $this->read(AvroDataIO::magic_size());
+
+ if (strlen($magic) < AvroDataIO::magic_size())
+ throw new AvroDataIOException(
+ 'Not an Avro data file: shorter than the Avro magic block');
+
+ if (AvroDataIO::magic() != $magic)
+ throw new AvroDataIOException(
+ sprintf('Not an Avro data file: %s does not match %s',
+ $magic, AvroDataIO::magic()));
+
+ $this->metadata = $this->datum_reader->read_data(AvroDataIO::metadata_schema(),
+ AvroDataIO::metadata_schema(),
+ $this->decoder);
+ $this->sync_marker = $this->read(AvroDataIO::SYNC_SIZE);
+ }
+
+ /**
+ * @internal Would be nice to implement data() as an iterator, I think
+ * @returns array of data from object container.
+ */
+ public function data()
+ {
+ $data = array();
+ while (true)
+ {
+ if (0 == $this->block_count)
+ {
+ if ($this->is_eof())
+ break;
+
+ if ($this->skip_sync())
+ if ($this->is_eof())
+ break;
+
+ $this->read_block_header();
+ }
+ $data []= $this->datum_reader->read($this->decoder);
+ $this->block_count -= 1;
+ }
+ return $data;
+ }
+
+ /**
+ * Closes this writer (and its AvroIO object.)
+ * @uses AvroIO::close()
+ */
+ public function close() { return $this->io->close(); }
+
+ /**
+ * @uses AvroIO::seek()
+ */
+ private function seek($offset, $whence)
+ {
+ return $this->io->seek($offset, $whence);
+ }
+
+ /**
+ * @uses AvroIO::read()
+ */
+ private function read($len) { return $this->io->read($len); }
+
+ /**
+ * @uses AvroIO::is_eof()
+ */
+ private function is_eof() { return $this->io->is_eof(); }
+
+ private function skip_sync()
+ {
+ $proposed_sync_marker = $this->read(AvroDataIO::SYNC_SIZE);
+ if ($proposed_sync_marker != $this->sync_marker)
+ {
+ $this->seek(-AvroDataIO::SYNC_SIZE, AvroIO::SEEK_CUR);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Reads the block header (which includes the count of items in the block
+ * and the length in bytes of the block)
+ * @returns int length in bytes of the block.
+ */
+ private function read_block_header()
+ {
+ $this->block_count = $this->decoder->read_long();
+ return $this->decoder->read_long();
+ }
+
+}
+
+/**
+ * Writes Avro data to an AvroIO source using an AvroSchema
+ * @package Avro
+ */
+class AvroDataIOWriter
+{
+ /**
+ * @returns string a new, unique sync marker.
+ */
+ private static function generate_sync_marker()
+ {
+ // From http://php.net/manual/en/function.mt-rand.php comments
+ return pack('S8',
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff) | 0x4000,
+ mt_rand(0, 0xffff) | 0x8000,
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
+ }
+
+ /**
+ * @var AvroIO object container where data is written
+ */
+ private $io;
+
+ /**
+ * @var AvroIOBinaryEncoder encoder for object container
+ */
+ private $encoder;
+
+ /**
+ * @var AvroDatumWriter
+ */
+ private $datum_writer;
+
+ /**
+ * @var AvroStringIO buffer for writing
+ */
+ private $buffer;
+
+ /**
+ * @var AvroIOBinaryEncoder encoder for buffer
+ */
+ private $buffer_encoder; // AvroIOBinaryEncoder
+
+ /**
+ * @var int count of items written to block
+ */
+ private $block_count;
+
+ /**
+ * @var array map of object container metadata
+ */
+ private $metadata;
+
+ /**
+ * @param AvroIO $io
+ * @param AvroIODatumWriter $datum_writer
+ * @param AvroSchema $writers_schema
+ */
+ public function __construct($io, $datum_writer, $writers_schema=null)
+ {
+ if (!($io instanceof AvroIO))
+ throw new AvroDataIOException('io must be instance of AvroIO');
+
+ $this->io = $io;
+ $this->encoder = new AvroIOBinaryEncoder($this->io);
+ $this->datum_writer = $datum_writer;
+ $this->buffer = new AvroStringIO();
+ $this->buffer_encoder = new AvroIOBinaryEncoder($this->buffer);
+ $this->block_count = 0;
+ $this->metadata = array();
+
+ if ($writers_schema)
+ {
+ $this->sync_marker = self::generate_sync_marker();
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR] = AvroDataIO::NULL_CODEC;
+ $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = strval($writers_schema);
+ $this->write_header();
+ }
+ else
+ {
+ $dfr = new AvroDataIOReader($this->io, new AvroIODatumReader());
+ $this->sync_marker = $dfr->sync_marker;
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR] = $dfr->metadata[AvroDataIO::METADATA_CODEC_ATTR];
+
+ $schema_from_file = $dfr->metadata[AvroDataIO::METADATA_SCHEMA_ATTR];
+ $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = $schema_from_file;
+ $this->datum_writer->writers_schema = AvroSchema::parse($schema_from_file);
+ $this->seek(0, SEEK_END);
+ }
+ }
+
+ /**
+ * @param mixed $datum
+ */
+ public function append($datum)
+ {
+ $this->datum_writer->write($datum, $this->buffer_encoder);
+ $this->block_count++;
+
+ if ($this->buffer->length() >= AvroDataIO::SYNC_INTERVAL)
+ $this->write_block();
+ }
+
+ /**
+ * Flushes buffer to AvroIO object container and closes it.
+ * @return mixed value of $io->close()
+ * @see AvroIO::close()
+ */
+ public function close()
+ {
+ $this->flush();
+ return $this->io->close();
+ }
+
+ /**
+ * Flushes biffer to AvroIO object container.
+ * @returns mixed value of $io->flush()
+ * @see AvroIO::flush()
+ */
+ private function flush()
+ {
+ $this->write_block();
+ return $this->io->flush();
+ }
+
+ /**
+ * Writes a block of data to the AvroIO object container.
+ * @throws AvroDataIOException if the codec provided by the encoder
+ * is not supported
+ * @internal Should the codec check happen in the constructor?
+ * Why wait until we're writing data?
+ */
+ private function write_block()
+ {
+ if ($this->block_count > 0)
+ {
+ $this->encoder->write_long($this->block_count);
+ $to_write = strval($this->buffer);
+ $this->encoder->write_long(strlen($to_write));
+
+ if (AvroDataIO::is_valid_codec(
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR]))
+ $this->write($to_write);
+ else
+ throw new AvroDataIOException(
+ sprintf('codec %s is not supported',
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR]));
+
+ $this->write($this->sync_marker);
+ $this->buffer->truncate();
+ $this->block_count = 0;
+ }
+ }
+
+ /**
+ * Writes the header of the AvroIO object container
+ */
+ private function write_header()
+ {
+ $this->write(AvroDataIO::magic());
+ $this->datum_writer->write_data(AvroDataIO::metadata_schema(),
+ $this->metadata, $this->encoder);
+ $this->write($this->sync_marker);
+ }
+
+ /**
+ * @param string $bytes
+ * @uses AvroIO::write()
+ */
+ private function write($bytes) { return $this->io->write($bytes); }
+
+ /**
+ * @param int $offset
+ * @param int $whence
+ * @uses AvroIO::seek()
+ */
+ private function seek($offset, $whence)
+ {
+ return $this->io->seek($offset, $whence);
+ }
+}
diff --git a/vendor/wikimedia/avro/lib/avro/datum.php b/vendor/wikimedia/avro/lib/avro/datum.php
new file mode 100644
index 00000000..ea275faf
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/datum.php
@@ -0,0 +1,984 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for reading and writing Avro data to AvroIO objects.
+ *
+ * @package Avro
+ *
+ * @todo Implement JSON encoding, as is required by the Avro spec.
+ */
+
+/**
+ * Exceptions arising from writing or reading Avro data.
+ *
+ * @package Avro
+ */
+class AvroIOTypeException extends AvroException
+{
+ /**
+ * @param AvroSchema $expected_schema
+ * @param mixed $datum
+ */
+ public function __construct($expected_schema, $datum)
+ {
+ parent::__construct(sprintf('The datum %s is not an example of schema %s',
+ var_export($datum, true), $expected_schema));
+ }
+}
+
+/**
+ * Exceptions arising from incompatibility between
+ * reader and writer schemas.
+ *
+ * @package Avro
+ */
+class AvroIOSchemaMatchException extends AvroException
+{
+ /**
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ */
+ function __construct($writers_schema, $readers_schema)
+ {
+ parent::__construct(
+ sprintf("Writer's schema %s and Reader's schema %s do not match.",
+ $writers_schema, $readers_schema));
+ }
+}
+
+/**
+ * Handles schema-specific writing of data to the encoder.
+ *
+ * Ensures that each datum written is consistent with the writer's schema.
+ *
+ * @package Avro
+ */
+class AvroIODatumWriter
+{
+ /**
+ * Schema used by this instance to write Avro data.
+ * @var AvroSchema
+ */
+ private $writers_schema;
+
+ /**
+ * @param AvroSchema $writers_schema
+ */
+ function __construct($writers_schema=null)
+ {
+ $this->writers_schema = $writers_schema;
+ }
+
+ /**
+ * @param AvroSchema $writers_schema
+ * @param $datum
+ * @param AvroIOBinaryEncoder $encoder
+ * @returns mixed
+ *
+ * @throws AvroIOTypeException if $datum is invalid for $writers_schema
+ */
+ function write_data($writers_schema, $datum, $encoder)
+ {
+ if (!AvroSchema::is_valid_datum($writers_schema, $datum))
+ throw new AvroIOTypeException($writers_schema, $datum);
+
+ switch ($writers_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return $encoder->write_null($datum);
+ case AvroSchema::BOOLEAN_TYPE:
+ return $encoder->write_boolean($datum);
+ case AvroSchema::INT_TYPE:
+ return $encoder->write_int($datum);
+ case AvroSchema::LONG_TYPE:
+ return $encoder->write_long($datum);
+ case AvroSchema::FLOAT_TYPE:
+ return $encoder->write_float($datum);
+ case AvroSchema::DOUBLE_TYPE:
+ return $encoder->write_double($datum);
+ case AvroSchema::STRING_TYPE:
+ return $encoder->write_string($datum);
+ case AvroSchema::BYTES_TYPE:
+ return $encoder->write_bytes($datum);
+ case AvroSchema::ARRAY_SCHEMA:
+ return $this->write_array($writers_schema, $datum, $encoder);
+ case AvroSchema::MAP_SCHEMA:
+ return $this->write_map($writers_schema, $datum, $encoder);
+ case AvroSchema::FIXED_SCHEMA:
+ return $this->write_fixed($writers_schema, $datum, $encoder);
+ case AvroSchema::ENUM_SCHEMA:
+ return $this->write_enum($writers_schema, $datum, $encoder);
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ case AvroSchema::REQUEST_SCHEMA:
+ return $this->write_record($writers_schema, $datum, $encoder);
+ case AvroSchema::UNION_SCHEMA:
+ return $this->write_union($writers_schema, $datum, $encoder);
+ default:
+ throw new AvroException(sprintf('Uknown type: %s',
+ $writers_schema->type));
+ }
+ }
+
+ /**
+ * @param $datum
+ * @param AvroIOBinaryEncoder $encoder
+ */
+ function write($datum, $encoder)
+ {
+ $this->write_data($this->writers_schema, $datum, $encoder);
+ }
+
+ /**#@+
+ * @param AvroSchema $writers_schema
+ * @param null|boolean|int|float|string|array $datum item to be written
+ * @param AvroIOBinaryEncoder $encoder
+ */
+ private function write_array($writers_schema, $datum, $encoder)
+ {
+ $datum_count = count($datum);
+ if (0 < $datum_count)
+ {
+ $encoder->write_long($datum_count);
+ $items = $writers_schema->items();
+ foreach ($datum as $item)
+ $this->write_data($items, $item, $encoder);
+ }
+ return $encoder->write_long(0);
+ }
+
+ private function write_map($writers_schema, $datum, $encoder)
+ {
+ $datum_count = count($datum);
+ if ($datum_count > 0)
+ {
+ $encoder->write_long($datum_count);
+ foreach ($datum as $k => $v)
+ {
+ $encoder->write_string($k);
+ $this->write_data($writers_schema->values(), $v, $encoder);
+ }
+ }
+ $encoder->write_long(0);
+ }
+
+ private function write_union($writers_schema, $datum, $encoder)
+ {
+ $datum_schema_index = -1;
+ $datum_schema = null;
+ foreach ($writers_schema->schemas() as $index => $schema)
+ if (AvroSchema::is_valid_datum($schema, $datum))
+ {
+ $datum_schema_index = $index;
+ $datum_schema = $schema;
+ break;
+ }
+
+ if (is_null($datum_schema))
+ throw new AvroIOTypeException($writers_schema, $datum);
+
+ $encoder->write_long($datum_schema_index);
+ $this->write_data($datum_schema, $datum, $encoder);
+ }
+
+ private function write_enum($writers_schema, $datum, $encoder)
+ {
+ $datum_index = $writers_schema->symbol_index($datum);
+ return $encoder->write_int($datum_index);
+ }
+
+ private function write_fixed($writers_schema, $datum, $encoder)
+ {
+ /**
+ * NOTE Unused $writers_schema parameter included for consistency
+ * with other write_* methods.
+ */
+ return $encoder->write($datum);
+ }
+
+ private function write_record($writers_schema, $datum, $encoder)
+ {
+ foreach ($writers_schema->fields() as $field)
+ $this->write_data($field->type(), $datum[$field->name()], $encoder);
+ }
+
+ /**#@-*/
+}
+
+/**
+ * Encodes and writes Avro data to an AvroIO object using
+ * Avro binary encoding.
+ *
+ * @package Avro
+ */
+class AvroIOBinaryEncoder
+{
+ /**
+ * Performs encoding of the given float value to a binary string
+ *
+ * XXX: This is <b>not</b> endian-aware! The {@link Avro::check_platform()}
+ * called in {@link AvroIOBinaryEncoder::__construct()} should ensure the
+ * library is only used on little-endian platforms, which ensure the little-endian
+ * encoding required by the Avro spec.
+ *
+ * @param float $float
+ * @returns string bytes
+ * @see Avro::check_platform()
+ */
+ static function float_to_int_bits($float)
+ {
+ return pack('f', (float) $float);
+ }
+
+ /**
+ * Performs encoding of the given double value to a binary string
+ *
+ * XXX: This is <b>not</b> endian-aware! See comments in
+ * {@link AvroIOBinaryEncoder::float_to_int_bits()} for details.
+ *
+ * @param double $double
+ * @returns string bytes
+ */
+ static function double_to_long_bits($double)
+ {
+ return pack('d', (double) $double);
+ }
+
+ /**
+ * @param int|string $n
+ * @returns string long $n encoded as bytes
+ * @internal This relies on 64-bit PHP.
+ */
+ static public function encode_long($n)
+ {
+ $n = (int) $n;
+ $n = ($n << 1) ^ ($n >> 63);
+ $str = '';
+ while (0 != ($n & ~0x7F))
+ {
+ $str .= chr(($n & 0x7F) | 0x80);
+ $n >>= 7;
+ }
+ $str .= chr($n);
+ return $str;
+ }
+
+ /**
+ * @var AvroIO
+ */
+ private $io;
+
+ /**
+ * @param AvroIO $io object to which data is to be written.
+ *
+ */
+ function __construct($io)
+ {
+ Avro::check_platform();
+ $this->io = $io;
+ }
+
+ /**
+ * @param null $datum actual value is ignored
+ */
+ function write_null($datum) { return null; }
+
+ /**
+ * @param boolean $datum
+ */
+ function write_boolean($datum)
+ {
+ $byte = $datum ? chr(1) : chr(0);
+ $this->write($byte);
+ }
+
+ /**
+ * @param int $datum
+ */
+ function write_int($datum) { $this->write_long($datum); }
+
+ /**
+ * @param int $n
+ */
+ function write_long($n)
+ {
+ if (Avro::uses_gmp())
+ $this->write(AvroGMP::encode_long($n));
+ else
+ $this->write(self::encode_long($n));
+ }
+
+ /**
+ * @param float $datum
+ * @uses self::float_to_int_bits()
+ */
+ public function write_float($datum)
+ {
+ $this->write(self::float_to_int_bits($datum));
+ }
+
+ /**
+ * @param float $datum
+ * @uses self::double_to_long_bits()
+ */
+ public function write_double($datum)
+ {
+ $this->write(self::double_to_long_bits($datum));
+ }
+
+ /**
+ * @param string $str
+ * @uses self::write_bytes()
+ */
+ function write_string($str) { $this->write_bytes($str); }
+
+ /**
+ * @param string $bytes
+ */
+ function write_bytes($bytes)
+ {
+ $this->write_long(strlen($bytes));
+ $this->write($bytes);
+ }
+
+ /**
+ * @param string $datum
+ */
+ function write($datum) { $this->io->write($datum); }
+}
+
+/**
+ * Handles schema-specifc reading of data from the decoder.
+ *
+ * Also handles schema resolution between the reader and writer
+ * schemas (if a writer's schema is provided).
+ *
+ * @package Avro
+ */
+class AvroIODatumReader
+{
+ /**
+ *
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ * @returns boolean true if the schemas are consistent with
+ * each other and false otherwise.
+ */
+ static function schemas_match($writers_schema, $readers_schema)
+ {
+ $writers_schema_type = $writers_schema->type;
+ $readers_schema_type = $readers_schema->type;
+
+ if (AvroSchema::UNION_SCHEMA == $writers_schema_type
+ || AvroSchema::UNION_SCHEMA == $readers_schema_type)
+ return true;
+
+ if ($writers_schema_type == $readers_schema_type)
+ {
+ if (AvroSchema::is_primitive_type($writers_schema_type))
+ return true;
+
+ switch ($readers_schema_type)
+ {
+ case AvroSchema::MAP_SCHEMA:
+ return self::attributes_match($writers_schema->values(),
+ $readers_schema->values(),
+ array(AvroSchema::TYPE_ATTR));
+ case AvroSchema::ARRAY_SCHEMA:
+ return self::attributes_match($writers_schema->items(),
+ $readers_schema->items(),
+ array(AvroSchema::TYPE_ATTR));
+ case AvroSchema::ENUM_SCHEMA:
+ return self::attributes_match($writers_schema, $readers_schema,
+ array(AvroSchema::FULLNAME_ATTR));
+ case AvroSchema::FIXED_SCHEMA:
+ return self::attributes_match($writers_schema, $readers_schema,
+ array(AvroSchema::FULLNAME_ATTR,
+ AvroSchema::SIZE_ATTR));
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ return self::attributes_match($writers_schema, $readers_schema,
+ array(AvroSchema::FULLNAME_ATTR));
+ case AvroSchema::REQUEST_SCHEMA:
+ // XXX: This seems wrong
+ return true;
+ // XXX: no default
+ }
+
+ if (AvroSchema::INT_TYPE == $writers_schema_type
+ && in_array($readers_schema_type, array(AvroSchema::LONG_TYPE,
+ AvroSchema::FLOAT_TYPE,
+ AvroSchema::DOUBLE_TYPE)))
+ return true;
+
+ if (AvroSchema::LONG_TYPE == $writers_schema_type
+ && in_array($readers_schema_type, array(AvroSchema::FLOAT_TYPE,
+ AvroSchema::DOUBLE_TYPE)))
+ return true;
+
+ if (AvroSchema::FLOAT_TYPE == $writers_schema_type
+ && AvroSchema::DOUBLE_TYPE == $readers_schema_type)
+ return true;
+
+ return false;
+ }
+
+ }
+
+ /**
+ * Checks equivalence of the given attributes of the two given schemas.
+ *
+ * @param AvroSchema $schema_one
+ * @param AvroSchema $schema_two
+ * @param string[] $attribute_names array of string attribute names to compare
+ *
+ * @returns boolean true if the attributes match and false otherwise.
+ */
+ static function attributes_match($schema_one, $schema_two, $attribute_names)
+ {
+ foreach ($attribute_names as $attribute_name)
+ if ($schema_one->attribute($attribute_name)
+ != $schema_two->attribute($attribute_name))
+ return false;
+ return true;
+ }
+
+ /**
+ * @var AvroSchema
+ */
+ private $writers_schema;
+
+ /**
+ * @var AvroSchema
+ */
+ private $readers_schema;
+
+ /**
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ */
+ function __construct($writers_schema=null, $readers_schema=null)
+ {
+ $this->writers_schema = $writers_schema;
+ $this->readers_schema = $readers_schema;
+ }
+
+ /**
+ * @param AvroSchema $readers_schema
+ */
+ public function set_writers_schema($readers_schema)
+ {
+ $this->writers_schema = $readers_schema;
+ }
+
+ /**
+ * @param AvroIOBinaryDecoder $decoder
+ * @returns string
+ */
+ public function read($decoder)
+ {
+ if (is_null($this->readers_schema))
+ $this->readers_schema = $this->writers_schema;
+ return $this->read_data($this->writers_schema, $this->readers_schema,
+ $decoder);
+ }
+
+ /**#@+
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ * @param AvroIOBinaryDecoder $decoder
+ */
+ /**
+ * @returns mixed
+ */
+ public function read_data($writers_schema, $readers_schema, $decoder)
+ {
+ if (!self::schemas_match($writers_schema, $readers_schema))
+ throw new AvroIOSchemaMatchException($writers_schema, $readers_schema);
+
+ // Schema resolution: reader's schema is a union, writer's schema is not
+ if (AvroSchema::UNION_SCHEMA == $readers_schema->type()
+ && AvroSchema::UNION_SCHEMA != $writers_schema->type())
+ {
+ foreach ($readers_schema->schemas() as $schema)
+ if (self::schemas_match($writers_schema, $schema))
+ return $this->read_data($writers_schema, $schema, $decoder);
+ throw new AvroIOSchemaMatchException($writers_schema, $readers_schema);
+ }
+
+ switch ($writers_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return $decoder->read_null();
+ case AvroSchema::BOOLEAN_TYPE:
+ return $decoder->read_boolean();
+ case AvroSchema::INT_TYPE:
+ return $decoder->read_int();
+ case AvroSchema::LONG_TYPE:
+ return $decoder->read_long();
+ case AvroSchema::FLOAT_TYPE:
+ return $decoder->read_float();
+ case AvroSchema::DOUBLE_TYPE:
+ return $decoder->read_double();
+ case AvroSchema::STRING_TYPE:
+ return $decoder->read_string();
+ case AvroSchema::BYTES_TYPE:
+ return $decoder->read_bytes();
+ case AvroSchema::ARRAY_SCHEMA:
+ return $this->read_array($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::MAP_SCHEMA:
+ return $this->read_map($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::UNION_SCHEMA:
+ return $this->read_union($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::ENUM_SCHEMA:
+ return $this->read_enum($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::FIXED_SCHEMA:
+ return $this->read_fixed($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ case AvroSchema::REQUEST_SCHEMA:
+ return $this->read_record($writers_schema, $readers_schema, $decoder);
+ default:
+ throw new AvroException(sprintf("Cannot read unknown schema type: %s",
+ $writers_schema->type()));
+ }
+ }
+
+ /**
+ * @returns array
+ */
+ public function read_array($writers_schema, $readers_schema, $decoder)
+ {
+ $items = array();
+ $block_count = $decoder->read_long();
+ while (0 != $block_count)
+ {
+ if ($block_count < 0)
+ {
+ $block_count = -$block_count;
+ $block_size = $decoder->read_long(); // Read (and ignore) block size
+ }
+ for ($i = 0; $i < $block_count; $i++)
+ $items []= $this->read_data($writers_schema->items(),
+ $readers_schema->items(),
+ $decoder);
+ $block_count = $decoder->read_long();
+ }
+ return $items;
+ }
+
+ /**
+ * @returns array
+ */
+ public function read_map($writers_schema, $readers_schema, $decoder)
+ {
+ $items = array();
+ $pair_count = $decoder->read_long();
+ while (0 != $pair_count)
+ {
+ if ($pair_count < 0)
+ {
+ $pair_count = -$pair_count;
+ // Note: we're not doing anything with block_size other than skipping it
+ $block_size = $decoder->read_long();
+ }
+
+ for ($i = 0; $i < $pair_count; $i++)
+ {
+ $key = $decoder->read_string();
+ $items[$key] = $this->read_data($writers_schema->values(),
+ $readers_schema->values(),
+ $decoder);
+ }
+ $pair_count = $decoder->read_long();
+ }
+ return $items;
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function read_union($writers_schema, $readers_schema, $decoder)
+ {
+ $schema_index = $decoder->read_long();
+ $selected_writers_schema = $writers_schema->schema_by_index($schema_index);
+ return $this->read_data($selected_writers_schema, $readers_schema, $decoder);
+ }
+
+ /**
+ * @returns string
+ */
+ public function read_enum($writers_schema, $readers_schema, $decoder)
+ {
+ $symbol_index = $decoder->read_int();
+ $symbol = $writers_schema->symbol_by_index($symbol_index);
+ if (!$readers_schema->has_symbol($symbol))
+ null; // FIXME: unset wrt schema resolution
+ return $symbol;
+ }
+
+ /**
+ * @returns string
+ */
+ public function read_fixed($writers_schema, $readers_schema, $decoder)
+ {
+ return $decoder->read($writers_schema->size());
+ }
+
+ /**
+ * @returns array
+ */
+ public function read_record($writers_schema, $readers_schema, $decoder)
+ {
+ $readers_fields = $readers_schema->fields_hash();
+ $record = array();
+ foreach ($writers_schema->fields() as $writers_field)
+ {
+ $type = $writers_field->type();
+ if (isset($readers_fields[$writers_field->name()]))
+ $record[$writers_field->name()]
+ = $this->read_data($type,
+ $readers_fields[$writers_field->name()]->type(),
+ $decoder);
+ else
+ $this->skip_data($type, $decoder);
+ }
+ // Fill in default values
+ if (count($readers_fields) > count($record))
+ {
+ $writers_fields = $writers_schema->fields_hash();
+ foreach ($readers_fields as $field_name => $field)
+ {
+ if (!isset($writers_fields[$field_name]))
+ {
+ if ($field->has_default_value())
+ $record[$field->name()]
+ = $this->read_default_value($field->type(),
+ $field->default_value());
+ else
+ null; // FIXME: unset
+ }
+ }
+ }
+
+ return $record;
+ }
+ /**#@-*/
+
+ /**
+ * @param AvroSchema $field_schema
+ * @param null|boolean|int|float|string|array $default_value
+ * @returns null|boolean|int|float|string|array
+ *
+ * @throws AvroException if $field_schema type is unknown.
+ */
+ public function read_default_value($field_schema, $default_value)
+ {
+ switch($field_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return null;
+ case AvroSchema::BOOLEAN_TYPE:
+ return $default_value;
+ case AvroSchema::INT_TYPE:
+ case AvroSchema::LONG_TYPE:
+ return (int) $default_value;
+ case AvroSchema::FLOAT_TYPE:
+ case AvroSchema::DOUBLE_TYPE:
+ return (float) $default_value;
+ case AvroSchema::STRING_TYPE:
+ case AvroSchema::BYTES_TYPE:
+ return $default_value;
+ case AvroSchema::ARRAY_SCHEMA:
+ $array = array();
+ foreach ($default_value as $json_val)
+ {
+ $val = $this->read_default_value($field_schema->items(), $json_val);
+ $array []= $val;
+ }
+ return $array;
+ case AvroSchema::MAP_SCHEMA:
+ $map = array();
+ foreach ($default_value as $key => $json_val)
+ $map[$key] = $this->read_default_value($field_schema->values(),
+ $json_val);
+ return $map;
+ case AvroSchema::UNION_SCHEMA:
+ return $this->read_default_value($field_schema->schema_by_index(0),
+ $default_value);
+ case AvroSchema::ENUM_SCHEMA:
+ case AvroSchema::FIXED_SCHEMA:
+ return $default_value;
+ case AvroSchema::RECORD_SCHEMA:
+ $record = array();
+ foreach ($field_schema->fields() as $field)
+ {
+ $field_name = $field->name();
+ if (!$json_val = $default_value[$field_name])
+ $json_val = $field->default_value();
+
+ $record[$field_name] = $this->read_default_value($field->type(),
+ $json_val);
+ }
+ return $record;
+ default:
+ throw new AvroException(sprintf('Unknown type: %s', $field_schema->type()));
+ }
+ }
+
+ /**
+ * @param AvroSchema $writers_schema
+ * @param AvroIOBinaryDecoder $decoder
+ */
+ private function skip_data($writers_schema, $decoder)
+ {
+ switch ($writers_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return $decoder->skip_null();
+ case AvroSchema::BOOLEAN_TYPE:
+ return $decoder->skip_boolean();
+ case AvroSchema::INT_TYPE:
+ return $decoder->skip_int();
+ case AvroSchema::LONG_TYPE:
+ return $decoder->skip_long();
+ case AvroSchema::FLOAT_TYPE:
+ return $decoder->skip_float();
+ case AvroSchema::DOUBLE_TYPE:
+ return $decoder->skip_double();
+ case AvroSchema::STRING_TYPE:
+ return $decoder->skip_string();
+ case AvroSchema::BYTES_TYPE:
+ return $decoder->skip_bytes();
+ case AvroSchema::ARRAY_SCHEMA:
+ return $decoder->skip_array($writers_schema, $decoder);
+ case AvroSchema::MAP_SCHEMA:
+ return $decoder->skip_map($writers_schema, $decoder);
+ case AvroSchema::UNION_SCHEMA:
+ return $decoder->skip_union($writers_schema, $decoder);
+ case AvroSchema::ENUM_SCHEMA:
+ return $decoder->skip_enum($writers_schema, $decoder);
+ case AvroSchema::FIXED_SCHEMA:
+ return $decoder->skip_fixed($writers_schema, $decoder);
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ case AvroSchema::REQUEST_SCHEMA:
+ return $decoder->skip_record($writers_schema, $decoder);
+ default:
+ throw new AvroException(sprintf('Uknown schema type: %s',
+ $writers_schema->type()));
+ }
+ }
+}
+
+/**
+ * Decodes and reads Avro data from an AvroIO object encoded using
+ * Avro binary encoding.
+ *
+ * @package Avro
+ */
+class AvroIOBinaryDecoder
+{
+
+ /**
+ * @param int[] array of byte ascii values
+ * @returns long decoded value
+ * @internal Requires 64-bit platform
+ */
+ public static function decode_long_from_array($bytes)
+ {
+ $b = array_shift($bytes);
+ $n = $b & 0x7f;
+ $shift = 7;
+ while (0 != ($b & 0x80))
+ {
+ $b = array_shift($bytes);
+ $n |= (($b & 0x7f) << $shift);
+ $shift += 7;
+ }
+ return (($n >> 1) ^ -($n & 1));
+ }
+
+ /**
+ * Performs decoding of the binary string to a float value.
+ *
+ * XXX: This is <b>not</b> endian-aware! See comments in
+ * {@link AvroIOBinaryEncoder::float_to_int_bits()} for details.
+ *
+ * @param string $bits
+ * @returns float
+ */
+ static public function int_bits_to_float($bits)
+ {
+ $float = unpack('f', $bits);
+ return (float) $float[1];
+ }
+
+ /**
+ * Performs decoding of the binary string to a double value.
+ *
+ * XXX: This is <b>not</b> endian-aware! See comments in
+ * {@link AvroIOBinaryEncoder::float_to_int_bits()} for details.
+ *
+ * @param string $bits
+ * @returns float
+ */
+ static public function long_bits_to_double($bits)
+ {
+ $double = unpack('d', $bits);
+ return (double) $double[1];
+ }
+
+ /**
+ * @var AvroIO
+ */
+ private $io;
+
+ /**
+ * @param AvroIO $io object from which to read.
+ */
+ public function __construct($io)
+ {
+ Avro::check_platform();
+ $this->io = $io;
+ }
+
+ /**
+ * @returns string the next byte from $this->io.
+ * @throws AvroException if the next byte cannot be read.
+ */
+ private function next_byte() { return $this->read(1); }
+
+ /**
+ * @returns null
+ */
+ public function read_null() { return null; }
+
+ /**
+ * @returns boolean
+ */
+ public function read_boolean()
+ {
+ return (boolean) (1 == ord($this->next_byte()));
+ }
+
+ /**
+ * @returns int
+ */
+ public function read_int() { return (int) $this->read_long(); }
+
+ /**
+ * @returns long
+ */
+ public function read_long()
+ {
+ $byte = ord($this->next_byte());
+ $bytes = array($byte);
+ while (0 != ($byte & 0x80))
+ {
+ $byte = ord($this->next_byte());
+ $bytes []= $byte;
+ }
+
+ if (Avro::uses_gmp())
+ return AvroGMP::decode_long_from_array($bytes);
+
+ return self::decode_long_from_array($bytes);
+ }
+
+ /**
+ * @returns float
+ */
+ public function read_float()
+ {
+ return self::int_bits_to_float($this->read(4));
+ }
+
+ /**
+ * @returns double
+ */
+ public function read_double()
+ {
+ return self::long_bits_to_double($this->read(8));
+ }
+
+ /**
+ * A string is encoded as a long followed by that many bytes
+ * of UTF-8 encoded character data.
+ * @returns string
+ */
+ public function read_string() { return $this->read_bytes(); }
+
+ /**
+ * @returns string
+ */
+ public function read_bytes() { return $this->read($this->read_long()); }
+
+ /**
+ * @param int $len count of bytes to read
+ * @returns string
+ */
+ public function read($len) { return $this->io->read($len); }
+
+ public function skip_null() { return null; }
+
+ public function skip_boolean() { return $this->skip(1); }
+
+ public function skip_int() { return $this->skip_long(); }
+
+ protected function skip_long()
+ {
+ $b = $this->next_byte();
+ while (0 != ($b & 0x80))
+ $b = $this->next_byte();
+ }
+
+ public function skip_float() { return $this->skip(4); }
+
+ public function skip_double() { return $this->skip(8); }
+
+ public function skip_bytes() { return $this->skip($this->read_long()); }
+
+ public function skip_string() { return $this->skip_bytes(); }
+
+ /**
+ * @param int $len count of bytes to skip
+ * @uses AvroIO::seek()
+ */
+ public function skip($len) { $this->seek($len, AvroIO::SEEK_CUR); }
+
+ /**
+ * @returns int position of pointer in AvroIO instance
+ * @uses AvroIO::tell()
+ */
+ private function tell() { return $this->io->tell(); }
+
+ /**
+ * @param int $offset
+ * @param int $whence
+ * @returns boolean true upon success
+ * @uses AvroIO::seek()
+ */
+ private function seek($offset, $whence)
+ {
+ return $this->io->seek($offset, $whence);
+ }
+}
+
diff --git a/vendor/wikimedia/avro/lib/avro/debug.php b/vendor/wikimedia/avro/lib/avro/debug.php
new file mode 100644
index 00000000..2278f19b
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/debug.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Avro library code debugging functions
+ * @package Avro
+ */
+class AvroDebug
+{
+
+ /**
+ * @var int high debug level
+ */
+ const DEBUG5 = 5;
+ /**
+ * @var int low debug level
+ */
+ const DEBUG1 = 1;
+ /**
+ * @var int current debug level
+ */
+ const DEBUG_LEVEL = self::DEBUG1;
+
+ /**
+ * @var int $debug_level
+ * @returns boolean true if the given $debug_level is equivalent
+ * or more verbose than than the current debug level
+ * and false otherwise.
+ */
+ static function is_debug($debug_level=self::DEBUG1)
+ {
+ return (self::DEBUG_LEVEL >= $debug_level);
+ }
+
+ /**
+ * @param string $format format string for the given arguments. Passed as is
+ * to <code>vprintf</code>.
+ * @param array $args array of arguments to pass to vsprinf.
+ * @param int $debug_level debug level at which to print this statement
+ * @returns boolean true
+ */
+ static function debug($format, $args, $debug_level=self::DEBUG1)
+ {
+ if (self::is_debug($debug_level))
+ vprintf($format . "\n", $args);
+ return true;
+ }
+
+ /**
+ * @param string $str
+ * @returns string[] array of hex representation of each byte of $str
+ */
+ static function hex_array($str) { return self::bytes_array($str); }
+
+ /**
+ * @param string $str
+ * @param string $joiner string used to join
+ * @returns string hex-represented bytes of each byte of $str
+ joined by $joiner
+ */
+ static function hex_string($str, $joiner=' ')
+ {
+ return join($joiner, self::hex_array($str));
+ }
+
+ /**
+ * @param string $str
+ * @param string $format format to represent bytes
+ * @returns string[] array of each byte of $str formatted using $format
+ */
+ static function bytes_array($str, $format='x%02x')
+ {
+ $x = array();
+ foreach (str_split($str) as $b)
+ $x []= sprintf($format, ord($b));
+ return $x;
+ }
+
+ /**
+ * @param string $str
+ * @returns string[] array of bytes of $str represented in decimal format ('%3d')
+ */
+ static function dec_array($str) { return self::bytes_array($str, '%3d'); }
+
+ /**
+ * @param string $str
+ * @param string $joiner string to join bytes of $str
+ * @returns string of bytes of $str represented in decimal format
+ * @uses dec_array()
+ */
+ static function dec_string($str, $joiner = ' ')
+ {
+ return join($joiner, self::dec_array($str));
+ }
+
+ /**
+ * @param string $str
+ * @param string $format one of 'ctrl', 'hex', or 'dec' for control,
+ hexadecimal, or decimal format for bytes.
+ - ctrl: ASCII control characters represented as text.
+ For example, the null byte is represented as 'NUL'.
+ Visible ASCII characters represent themselves, and
+ others are represented as a decimal ('%03d')
+ - hex: bytes represented in hexadecimal ('%02X')
+ - dec: bytes represented in decimal ('%03d')
+ * @returns string[] array of bytes represented in the given format.
+ */
+ static function ascii_array($str, $format='ctrl')
+ {
+ if (!in_array($format, array('ctrl', 'hex', 'dec')))
+ throw new AvroException('Unrecognized format specifier');
+
+ $ctrl_chars = array('NUL', 'SOH', 'STX', 'ETX', 'EOT', 'ENQ', 'ACK', 'BEL',
+ 'BS', 'HT', 'LF', 'VT', 'FF', 'CR', 'SO', 'SI',
+ 'DLE', 'DC1', 'DC2', 'DC3', 'DC4', 'NAK', 'SYN', 'ETB',
+ 'CAN', 'EM', 'SUB', 'ESC', 'FS', 'GS', 'RS', 'US');
+ $x = array();
+ foreach (str_split($str) as $b)
+ {
+ $db = ord($b);
+ if ($db < 32)
+ {
+ switch ($format)
+ {
+ case 'ctrl':
+ $x []= str_pad($ctrl_chars[$db], 3, ' ', STR_PAD_LEFT);
+ break;
+ case 'hex':
+ $x []= sprintf("x%02X", $db);
+ break;
+ case 'dec':
+ $x []= str_pad($db, 3, '0', STR_PAD_LEFT);
+ break;
+ }
+ }
+ else if ($db < 127)
+ $x []= " $b";
+ else if ($db == 127)
+ {
+ switch ($format)
+ {
+ case 'ctrl':
+ $x []= 'DEL';
+ break;
+ case 'hex':
+ $x []= sprintf("x%02X", $db);
+ break;
+ case 'dec':
+ $x []= str_pad($db, 3, '0', STR_PAD_LEFT);
+ break;
+ }
+ }
+ else
+ if ('hex' == $format)
+ $x []= sprintf("x%02X", $db);
+ else
+ $x []= str_pad($db, 3, '0', STR_PAD_LEFT);
+ }
+ return $x;
+ }
+
+ /**
+ * @param string $str
+ * @param string $format one of 'ctrl', 'hex', or 'dec'.
+ * See {@link self::ascii_array()} for more description
+ * @param string $joiner
+ * @returns string of bytes joined by $joiner
+ * @uses ascii_array()
+ */
+ static function ascii_string($str, $format='ctrl', $joiner = ' ')
+ {
+ return join($joiner, self::ascii_array($str, $format));
+ }
+}
diff --git a/vendor/wikimedia/avro/lib/avro/gmp.php b/vendor/wikimedia/avro/lib/avro/gmp.php
new file mode 100644
index 00000000..3d41d034
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/gmp.php
@@ -0,0 +1,222 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Methods for handling 64-bit operations using the GMP extension.
+ *
+ * This is a naive and hackish implementation that is intended
+ * to work well enough to support Avro. It has not been tested
+ * beyond what's needed to decode and encode long values.
+ *
+ * @package Avro
+ */
+class AvroGMP {
+
+ /**
+ * @var resource memoized GMP resource for zero
+ */
+ private static $gmp_0;
+
+ /**
+ * @returns resource GMP resource for zero
+ */
+ private static function gmp_0()
+ {
+ if (!isset(self::$gmp_0))
+ self::$gmp_0 = gmp_init('0');
+ return self::$gmp_0;
+ }
+
+ /**
+ * @var resource memoized GMP resource for one (1)
+ */
+ private static $gmp_1;
+
+ /**
+ * @returns resource GMP resource for one (1)
+ */
+ private static function gmp_1()
+ {
+ if (!isset(self::$gmp_1))
+ self::$gmp_1 = gmp_init('1');
+ return self::$gmp_1;
+ }
+
+ /**
+ * @var resource memoized GMP resource for two (2)
+ */
+ private static $gmp_2;
+
+ /**
+ * @returns resource GMP resource for two (2)
+ */
+ private static function gmp_2()
+ {
+ if (!isset(self::$gmp_2))
+ self::$gmp_2 = gmp_init('2');
+ return self::$gmp_2;
+ }
+
+ /**
+ * @var resource memoized GMP resource for 0x7f
+ */
+ private static $gmp_0x7f;
+
+ /**
+ * @returns resource GMP resource for 0x7f
+ */
+ private static function gmp_0x7f()
+ {
+ if (!isset(self::$gmp_0x7f))
+ self::$gmp_0x7f = gmp_init('0x7f');
+ return self::$gmp_0x7f;
+ }
+
+ /**
+ * @var resource memoized GMP resource for 64-bit ~0x7f
+ */
+ private static $gmp_n0x7f;
+
+ /**
+ * @returns resource GMP resource for 64-bit ~0x7f
+ */
+ private static function gmp_n0x7f()
+ {
+ if (!isset(self::$gmp_n0x7f))
+ self::$gmp_n0x7f = gmp_init('0xffffffffffffff80');
+ return self::$gmp_n0x7f;
+ }
+
+ /**
+ * @var resource memoized GMP resource for 64-bits of 1
+ */
+ private static $gmp_0xfs;
+
+ /**
+ * @returns resource GMP resource for 64-bits of 1
+ */
+ private static function gmp_0xfs()
+ {
+ if (!isset(self::$gmp_0xfs))
+ self::$gmp_0xfs = gmp_init('0xffffffffffffffff');
+ return self::$gmp_0xfs;
+ }
+
+ /**
+ * @param GMP resource
+ * @returns GMP resource 64-bit two's complement of input.
+ */
+ static function gmp_twos_complement($g)
+ {
+ return gmp_neg(gmp_sub(gmp_pow(self::gmp_2(), 64), $g));
+ }
+
+ /**
+ * @interal Only works up to shift 63 (doesn't wrap bits around).
+ * @param resource|int|string $g
+ * @param int $shift number of bits to shift left
+ * @returns resource $g shifted left
+ */
+ static function shift_left($g, $shift)
+ {
+ if (0 == $shift)
+ return $g;
+
+ if (0 > gmp_sign($g))
+ $g = self::gmp_twos_complement($g);
+
+ $m = gmp_mul($g, gmp_pow(self::gmp_2(), $shift));
+ $m = gmp_and($m, self::gmp_0xfs());
+ if (gmp_testbit($m, 63))
+ $m = gmp_neg(gmp_add(gmp_and(gmp_com($m), self::gmp_0xfs()),
+ self::gmp_1()));
+ return $m;
+ }
+
+ /**
+ * Arithmetic right shift
+ * @param resource|int|string $g
+ * @param int $shift number of bits to shift right
+ * @returns resource $g shifted right $shift bits
+ */
+ static function shift_right($g, $shift)
+ {
+ if (0 == $shift)
+ return $g;
+
+ if (0 <= gmp_sign($g))
+ $m = gmp_div($g, gmp_pow(self::gmp_2(), $shift));
+ else // negative
+ {
+ $g = gmp_and($g, self::gmp_0xfs());
+ $m = gmp_div($g, gmp_pow(self::gmp_2(), $shift));
+ $m = gmp_and($m, self::gmp_0xfs());
+ for ($i = 63; $i >= (63 - $shift); $i--)
+ gmp_setbit($m, $i);
+
+ $m = gmp_neg(gmp_add(gmp_and(gmp_com($m), self::gmp_0xfs()),
+ self::gmp_1()));
+ }
+
+ return $m;
+ }
+
+ /**
+ * @param int|str $n integer (or string representation of integer) to encode
+ * @return string $bytes of the long $n encoded per the Avro spec
+ */
+ static function encode_long($n)
+ {
+ $g = gmp_init($n);
+ $g = gmp_xor(self::shift_left($g, 1),
+ self::shift_right($g, 63));
+ $bytes = '';
+ while (0 != gmp_cmp(self::gmp_0(), gmp_and($g, self::gmp_n0x7f())))
+ {
+ $bytes .= chr(gmp_intval(gmp_and($g, self::gmp_0x7f())) | 0x80);
+ $g = self::shift_right($g, 7);
+ }
+ $bytes .= chr(gmp_intval($g));
+ return $bytes;
+ }
+
+ /**
+ * @param int[] $bytes array of ascii codes of bytes to decode
+ * @return string represenation of decoded long.
+ */
+ static function decode_long_from_array($bytes)
+ {
+ $b = array_shift($bytes);
+ $g = gmp_init($b & 0x7f);
+ $shift = 7;
+ while (0 != ($b & 0x80))
+ {
+ $b = array_shift($bytes);
+ $g = gmp_or($g, self::shift_left(($b & 0x7f), $shift));
+ $shift += 7;
+ }
+ $val = gmp_xor(self::shift_right($g, 1), gmp_neg(gmp_and($g, 1)));
+ return gmp_strval($val);
+ }
+
+}
diff --git a/vendor/wikimedia/avro/lib/avro/io.php b/vendor/wikimedia/avro/lib/avro/io.php
new file mode 100644
index 00000000..239e53d8
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/io.php
@@ -0,0 +1,494 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Avro IO object classes
+ * @package Avro
+ */
+
+/**
+ * Exceptions associated with AvroIO instances.
+ * @package Avro
+ */
+class AvroIOException extends AvroException {}
+
+/**
+ * Barebones IO base class to provide common interface for file and string
+ * access within the Avro classes.
+ *
+ * @package Avro
+ */
+class AvroIO
+{
+
+ /**
+ * @var string general read mode
+ */
+ const READ_MODE = 'r';
+ /**
+ * @var string general write mode.
+ */
+ const WRITE_MODE = 'w';
+
+ /**
+ * @var int set position equal to $offset bytes
+ */
+ const SEEK_CUR = SEEK_CUR;
+ /**
+ * @var int set position to current index + $offset bytes
+ */
+ const SEEK_SET = SEEK_SET;
+ /**
+ * @var int set position to end of file + $offset bytes
+ */
+ const SEEK_END = SEEK_END;
+
+ /**
+ * Read $len bytes from AvroIO instance
+ * @var int $len
+ * @return string bytes read
+ */
+ public function read($len)
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Append bytes to this buffer. (Nothing more is needed to support Avro.)
+ * @param str $arg bytes to write
+ * @returns int count of bytes written.
+ * @throws AvroIOException if $args is not a string value.
+ */
+ public function write($arg)
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Return byte offset within AvroIO instance
+ * @return int
+ */
+ public function tell()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Set the position indicator. The new position, measured in bytes
+ * from the beginning of the file, is obtained by adding $offset to
+ * the position specified by $whence.
+ *
+ * @param int $offset
+ * @param int $whence one of AvroIO::SEEK_SET, AvroIO::SEEK_CUR,
+ * or Avro::SEEK_END
+ * @returns boolean true
+ *
+ * @throws AvroIOException
+ */
+ public function seek($offset, $whence=self::SEEK_SET)
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Flushes any buffered data to the AvroIO object.
+ * @returns boolean true upon success.
+ */
+ public function flush()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Returns whether or not the current position at the end of this AvroIO
+ * instance.
+ *
+ * Note is_eof() is <b>not</b> like eof in C or feof in PHP:
+ * it returns TRUE if the *next* read would be end of file,
+ * rather than if the *most recent* read read end of file.
+ * @returns boolean true if at the end of file, and false otherwise
+ */
+ public function is_eof()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Closes this AvroIO instance.
+ */
+ public function close()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+}
+
+/**
+ * AvroIO wrapper for string access
+ * @package Avro
+ */
+class AvroStringIO extends AvroIO
+{
+ /**
+ * @var string
+ */
+ private $string_buffer;
+ /**
+ * @var int current position in string
+ */
+ private $current_index;
+ /**
+ * @var boolean whether or not the string is closed.
+ */
+ private $is_closed;
+
+ /**
+ * @param string $str initial value of AvroStringIO buffer. Regardless
+ * of the initial value, the pointer is set to the
+ * beginning of the buffer.
+ * @throws AvroIOException if a non-string value is passed as $str
+ */
+ public function __construct($str = '')
+ {
+ $this->is_closed = false;
+ $this->string_buffer = '';
+ $this->current_index = 0;
+
+ if (is_string($str))
+ $this->string_buffer .= $str;
+ else
+ throw new AvroIOException(
+ sprintf('constructor argument must be a string: %s', gettype($str)));
+ }
+
+ /**
+ * Append bytes to this buffer.
+ * (Nothing more is needed to support Avro.)
+ * @param str $arg bytes to write
+ * @returns int count of bytes written.
+ * @throws AvroIOException if $args is not a string value.
+ */
+ public function write($arg)
+ {
+ $this->check_closed();
+ if (is_string($arg))
+ return $this->append_str($arg);
+ throw new AvroIOException(
+ sprintf('write argument must be a string: (%s) %s',
+ gettype($arg), var_export($arg, true)));
+ }
+
+ /**
+ * @returns string bytes read from buffer
+ * @todo test for fencepost errors wrt updating current_index
+ */
+ public function read($len)
+ {
+ $this->check_closed();
+ $read='';
+ for($i=$this->current_index; $i<($this->current_index+$len); $i++)
+ $read .= $this->string_buffer[$i];
+ if (strlen($read) < $len)
+ $this->current_index = $this->length();
+ else
+ $this->current_index += $len;
+ return $read;
+ }
+
+ /**
+ * @returns boolean true if successful
+ * @throws AvroIOException if the seek failed.
+ */
+ public function seek($offset, $whence=self::SEEK_SET)
+ {
+ if (!is_int($offset))
+ throw new AvroIOException('Seek offset must be an integer.');
+ // Prevent seeking before BOF
+ switch ($whence)
+ {
+ case self::SEEK_SET:
+ if (0 > $offset)
+ throw new AvroIOException('Cannot seek before beginning of file.');
+ $this->current_index = $offset;
+ break;
+ case self::SEEK_CUR:
+ if (0 > $this->current_index + $whence)
+ throw new AvroIOException('Cannot seek before beginning of file.');
+ $this->current_index += $offset;
+ break;
+ case self::SEEK_END:
+ if (0 > $this->length() + $offset)
+ throw new AvroIOException('Cannot seek before beginning of file.');
+ $this->current_index = $this->length() + $offset;
+ break;
+ default:
+ throw new AvroIOException(sprintf('Invalid seek whence %d', $whence));
+ }
+
+ return true;
+ }
+
+ /**
+ * @returns int
+ * @see AvroIO::tell()
+ */
+ public function tell() { return $this->current_index; }
+
+ /**
+ * @returns boolean
+ * @see AvroIO::is_eof()
+ */
+ public function is_eof()
+ {
+ return ($this->current_index >= $this->length());
+ }
+
+ /**
+ * No-op provided for compatibility with AvroIO interface.
+ * @returns boolean true
+ */
+ public function flush() { return true; }
+
+ /**
+ * Marks this buffer as closed.
+ * @returns boolean true
+ */
+ public function close()
+ {
+ $this->check_closed();
+ $this->is_closed = true;
+ return true;
+ }
+
+ /**
+ * @throws AvroIOException if the buffer is closed.
+ */
+ private function check_closed()
+ {
+ if ($this->is_closed())
+ throw new AvroIOException('Buffer is closed');
+ }
+
+ /**
+ * Appends bytes to this buffer.
+ * @param string $str
+ * @returns integer count of bytes written.
+ */
+ private function append_str($str)
+ {
+ $this->check_closed();
+ $this->string_buffer .= $str;
+ $len = strlen($str);
+ $this->current_index += $len;
+ return $len;
+ }
+
+ /**
+ * Truncates the truncate buffer to 0 bytes and returns the pointer
+ * to the beginning of the buffer.
+ * @returns boolean true
+ */
+ public function truncate()
+ {
+ $this->check_closed();
+ $this->string_buffer = '';
+ $this->current_index = 0;
+ return true;
+ }
+
+ /**
+ * @returns int count of bytes in the buffer
+ * @internal Could probably memoize length for performance, but
+ * no need do this yet.
+ */
+ public function length() { return strlen($this->string_buffer); }
+
+ /**
+ * @returns string
+ */
+ public function __toString() { return $this->string_buffer; }
+
+
+ /**
+ * @returns string
+ * @uses self::__toString()
+ */
+ public function string() { return $this->__toString(); }
+
+ /**
+ * @returns boolean true if this buffer is closed and false
+ * otherwise.
+ */
+ public function is_closed() { return $this->is_closed; }
+}
+
+/**
+ * AvroIO wrapper for PHP file access functions
+ * @package Avro
+ */
+class AvroFile extends AvroIO
+{
+ /**
+ * @var string fopen read mode value. Used internally.
+ */
+ const FOPEN_READ_MODE = 'rb';
+
+ /**
+ * @var string fopen write mode value. Used internally.
+ */
+ const FOPEN_WRITE_MODE = 'wb';
+
+ /**
+ * @var string
+ */
+ private $file_path;
+
+ /**
+ * @var resource file handle for AvroFile instance
+ */
+ private $file_handle;
+
+ public function __construct($file_path, $mode = self::READ_MODE)
+ {
+ /**
+ * XXX: should we check for file existence (in case of reading)
+ * or anything else about the provided file_path argument?
+ */
+ $this->file_path = $file_path;
+ switch ($mode)
+ {
+ case self::WRITE_MODE:
+ $this->file_handle = fopen($this->file_path, self::FOPEN_WRITE_MODE);
+ if (false == $this->file_handle)
+ throw new AvroIOException('Could not open file for writing');
+ break;
+ case self::READ_MODE:
+ $this->file_handle = fopen($this->file_path, self::FOPEN_READ_MODE);
+ if (false == $this->file_handle)
+ throw new AvroIOException('Could not open file for reading');
+ break;
+ default:
+ throw new AvroIOException(
+ sprintf("Only modes '%s' and '%s' allowed. You provided '%s'.",
+ self::READ_MODE, self::WRITE_MODE, $mode));
+ }
+ }
+
+ /**
+ * @returns int count of bytes written
+ * @throws AvroIOException if write failed.
+ */
+ public function write($str)
+ {
+ $len = fwrite($this->file_handle, $str);
+ if (false === $len)
+ throw new AvroIOException(sprintf('Could not write to file'));
+ return $len;
+ }
+
+ /**
+ * @param int $len count of bytes to read.
+ * @returns string bytes read
+ * @throws AvroIOException if length value is negative or if the read failed
+ */
+ public function read($len)
+ {
+ if (0 > $len)
+ throw new AvroIOException(
+ sprintf("Invalid length value passed to read: %d", $len));
+
+ if (0 == $len)
+ return '';
+
+ $bytes = fread($this->file_handle, $len);
+ if (false === $bytes)
+ throw new AvroIOException('Could not read from file');
+ return $bytes;
+ }
+
+ /**
+ * @returns int current position within the file
+ * @throws AvroFileExcpetion if tell failed.
+ */
+ public function tell()
+ {
+ $position = ftell($this->file_handle);
+ if (false === $position)
+ throw new AvroIOException('Could not execute tell on reader');
+ return $position;
+ }
+
+ /**
+ * @param int $offset
+ * @param int $whence
+ * @returns boolean true upon success
+ * @throws AvroIOException if seek failed.
+ * @see AvroIO::seek()
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ $res = fseek($this->file_handle, $offset, $whence);
+ // Note: does not catch seeking beyond end of file
+ if (-1 === $res)
+ throw new AvroIOException(
+ sprintf("Could not execute seek (offset = %d, whence = %d)",
+ $offset, $whence));
+ return true;
+ }
+
+ /**
+ * Closes the file.
+ * @returns boolean true if successful.
+ * @throws AvroIOException if there was an error closing the file.
+ */
+ public function close()
+ {
+ $res = fclose($this->file_handle);
+ if (false === $res)
+ throw new AvroIOException('Error closing file.');
+ return $res;
+ }
+
+ /**
+ * @returns boolean true if the pointer is at the end of the file,
+ * and false otherwise.
+ * @see AvroIO::is_eof() as behavior differs from feof()
+ */
+ public function is_eof()
+ {
+ $this->read(1);
+ if (feof($this->file_handle))
+ return true;
+ $this->seek(-1, self::SEEK_CUR);
+ return false;
+ }
+
+ /**
+ * @returns boolean true if the flush was successful.
+ * @throws AvroIOException if there was an error flushing the file.
+ */
+ public function flush()
+ {
+ $res = fflush($this->file_handle);
+ if (false === $res)
+ throw new AvroIOException('Could not flush file.');
+ return true;
+ }
+
+}
diff --git a/vendor/wikimedia/avro/lib/avro/protocol.php b/vendor/wikimedia/avro/lib/avro/protocol.php
new file mode 100644
index 00000000..a558e66b
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/protocol.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Avro library for protocols
+ * @package Avro
+ */
+class AvroProtocol
+{
+ public $name;
+ public $namespace;
+ public $schemata;
+
+ public static function parse($json)
+ {
+ if (is_null($json))
+ throw new AvroProtocolParseException( "Protocol can't be null");
+
+ $protocol = new AvroProtocol();
+ $protocol->real_parse(json_decode($json, true));
+ return $protocol;
+ }
+
+ function real_parse($avro) {
+ $this->protocol = $avro["protocol"];
+ $this->namespace = $avro["namespace"];
+ $this->schemata = new AvroNamedSchemata();
+ $this->name = $avro["protocol"];
+
+ if (!is_null($avro["types"])) {
+ $types = AvroSchema::real_parse($avro["types"], $this->namespace, $this->schemata);
+ }
+
+ if (!is_null($avro["messages"])) {
+ foreach ($avro["messages"] as $messageName => $messageAvro) {
+ $message = new AvroProtocolMessage($messageName, $messageAvro, $this);
+ $this->messages{$messageName} = $message;
+ }
+ }
+ }
+}
+
+class AvroProtocolMessage
+{
+ /**
+ * @var AvroRecordSchema $request
+ */
+
+ public $request;
+
+ public $response;
+
+ public function __construct($name, $avro, $protocol)
+ {
+ $this->name = $name;
+ $this->request = new AvroRecordSchema(new AvroName($name, null, $protocol->namespace), null, $avro{'request'}, $protocol->schemata, AvroSchema::REQUEST_SCHEMA);
+
+ if (array_key_exists('response', $avro)) {
+ $this->response = $protocol->schemata->schema_by_name(new AvroName($avro{'response'}, $protocol->namespace, $protocol->namespace));
+ if ($this->response == null)
+ $this->response = new AvroPrimitiveSchema($avro{'response'});
+ }
+ }
+}
+
+class AvroProtocolParseException extends AvroException {};
diff --git a/vendor/wikimedia/avro/lib/avro/schema.php b/vendor/wikimedia/avro/lib/avro/schema.php
new file mode 100644
index 00000000..3d7fbbb8
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/schema.php
@@ -0,0 +1,1457 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Avro Schema and and Avro Schema support classes.
+ * @package Avro
+ */
+
+/** TODO
+ * - ARRAY have only type and item attributes (what about metadata?)
+ * - MAP keys are (assumed?) to be strings
+ * - FIXED size must be integer (must be positive? less than MAXINT?)
+ * - primitive type names cannot have a namespace (so throw an error? or ignore?)
+ * - schema may contain multiple definitions of a named schema
+ * if definitions are equivalent (?)
+ * - Cleanup default namespace and named schemata handling.
+ * - For one, it appears to be *too* global. According to the spec,
+ * we should only be referencing schemas that are named within the
+ * *enclosing* schema, so those in sibling schemas (say, unions or fields)
+ * shouldn't be referenced, if I understand the spec correctly.
+ * - Also, if a named schema is defined more than once in the same schema,
+ * it must have the same definition: so it appears we *do* need to keep
+ * track of named schemata globally as well. (And does this play well
+ * with the requirements regarding enclosing schema?
+ * - default values for bytes and fixed fields are JSON strings,
+ * where unicode code points 0-255 are mapped to unsigned 8-bit byte values 0-255
+ * - make sure other default values for other schema are of appropriate type
+ * - Should AvroField really be an AvroSchema object? Avro Fields have a name
+ * attribute, but not a namespace attribute (and the name can't be namespace
+ * qualified). It also has additional attributes such as doc, which named schemas
+ * enum and record have (though not fixed schemas, which also have names), and
+ * fields also have default and order attributes, shared by no other schema type.
+ */
+
+/**
+ * Exceptions associated with parsing JSON schema represenations
+ * @package Avro
+ */
+class AvroSchemaParseException extends AvroException {};
+
+/**
+ * @package Avro
+ */
+class AvroSchema
+{
+ /**
+ * @var int lower bound of integer values: -(1 << 31)
+ */
+ const INT_MIN_VALUE = -2147483648;
+
+ /**
+ * @var int upper bound of integer values: (1 << 31) - 1
+ */
+ const INT_MAX_VALUE = 2147483647;
+
+ /**
+ * @var long lower bound of long values: -(1 << 63)
+ */
+ const LONG_MIN_VALUE = -9223372036854775808;
+
+ /**
+ * @var long upper bound of long values: (1 << 63) - 1
+ */
+ const LONG_MAX_VALUE = 9223372036854775807;
+
+ /**
+ * @var string null schema type name
+ */
+ const NULL_TYPE = 'null';
+
+ /**
+ * @var string boolean schema type name
+ */
+ const BOOLEAN_TYPE = 'boolean';
+
+ /**
+ * int schema type value is a 32-bit signed int
+ * @var string int schema type name.
+ */
+ const INT_TYPE = 'int';
+
+ /**
+ * long schema type value is a 64-bit signed int
+ * @var string long schema type name
+ */
+ const LONG_TYPE = 'long';
+
+ /**
+ * float schema type value is a 32-bit IEEE 754 floating-point number
+ * @var string float schema type name
+ */
+ const FLOAT_TYPE = 'float';
+
+ /**
+ * double schema type value is a 64-bit IEEE 754 floating-point number
+ * @var string double schema type name
+ */
+ const DOUBLE_TYPE = 'double';
+
+ /**
+ * string schema type value is a Unicode character sequence
+ * @var string string schema type name
+ */
+ const STRING_TYPE = 'string';
+
+ /**
+ * bytes schema type value is a sequence of 8-bit unsigned bytes
+ * @var string bytes schema type name
+ */
+ const BYTES_TYPE = 'bytes';
+
+ // Complex Types
+ // Unnamed Schema
+ /**
+ * @var string array schema type name
+ */
+ const ARRAY_SCHEMA = 'array';
+
+ /**
+ * @var string map schema type name
+ */
+ const MAP_SCHEMA = 'map';
+
+ /**
+ * @var string union schema type name
+ */
+ const UNION_SCHEMA = 'union';
+
+ /**
+ * Unions of error schemas are used by Avro messages
+ * @var string error_union schema type name
+ */
+ const ERROR_UNION_SCHEMA = 'error_union';
+
+ // Named Schema
+
+ /**
+ * @var string enum schema type name
+ */
+ const ENUM_SCHEMA = 'enum';
+
+ /**
+ * @var string fixed schema type name
+ */
+ const FIXED_SCHEMA = 'fixed';
+
+ /**
+ * @var string record schema type name
+ */
+ const RECORD_SCHEMA = 'record';
+ // Other Schema
+
+ /**
+ * @var string error schema type name
+ */
+ const ERROR_SCHEMA = 'error';
+
+ /**
+ * @var string request schema type name
+ */
+ const REQUEST_SCHEMA = 'request';
+
+
+ // Schema attribute names
+ /**
+ * @var string schema type name attribute name
+ */
+ const TYPE_ATTR = 'type';
+
+ /**
+ * @var string named schema name attribute name
+ */
+ const NAME_ATTR = 'name';
+
+ /**
+ * @var string named schema namespace attribute name
+ */
+ const NAMESPACE_ATTR = 'namespace';
+
+ /**
+ * @var string derived attribute: doesn't appear in schema
+ */
+ const FULLNAME_ATTR = 'fullname';
+
+ /**
+ * @var string array schema size attribute name
+ */
+ const SIZE_ATTR = 'size';
+
+ /**
+ * @var string record fields attribute name
+ */
+ const FIELDS_ATTR = 'fields';
+
+ /**
+ * @var string array schema items attribute name
+ */
+ const ITEMS_ATTR = 'items';
+
+ /**
+ * @var string enum schema symbols attribute name
+ */
+ const SYMBOLS_ATTR = 'symbols';
+
+ /**
+ * @var string map schema values attribute name
+ */
+ const VALUES_ATTR = 'values';
+
+ /**
+ * @var string document string attribute name
+ */
+ const DOC_ATTR = 'doc';
+
+ /**
+ * @var array list of primitive schema type names
+ */
+ private static $primitive_types = array(self::NULL_TYPE, self::BOOLEAN_TYPE,
+ self::STRING_TYPE, self::BYTES_TYPE,
+ self::INT_TYPE, self::LONG_TYPE,
+ self::FLOAT_TYPE, self::DOUBLE_TYPE);
+
+ /**
+ * @var array list of named schema type names
+ */
+ private static $named_types = array(self::FIXED_SCHEMA, self::ENUM_SCHEMA,
+ self::RECORD_SCHEMA, self::ERROR_SCHEMA);
+
+ /**
+ * @param string $type a schema type name
+ * @returns boolean true if the given type name is a named schema type name
+ * and false otherwise.
+ */
+ public static function is_named_type($type)
+ {
+ return in_array($type, self::$named_types);
+ }
+
+ /**
+ * @param string $type a schema type name
+ * @returns boolean true if the given type name is a primitive schema type
+ * name and false otherwise.
+ */
+ public static function is_primitive_type($type)
+ {
+ return in_array($type, self::$primitive_types);
+ }
+
+ /**
+ * @param string $type a schema type name
+ * @returns boolean true if the given type name is a valid schema type
+ * name and false otherwise.
+ */
+ public static function is_valid_type($type)
+ {
+ return (self::is_primitive_type($type)
+ || self::is_named_type($type)
+ || in_array($type, array(self::ARRAY_SCHEMA,
+ self::MAP_SCHEMA,
+ self::UNION_SCHEMA,
+ self::REQUEST_SCHEMA,
+ self::ERROR_UNION_SCHEMA)));
+ }
+
+ /**
+ * @var array list of names of reserved attributes
+ */
+ private static $reserved_attrs = array(self::TYPE_ATTR,
+ self::NAME_ATTR,
+ self::NAMESPACE_ATTR,
+ self::FIELDS_ATTR,
+ self::ITEMS_ATTR,
+ self::SIZE_ATTR,
+ self::SYMBOLS_ATTR,
+ self::VALUES_ATTR);
+
+ /**
+ * @param string $json JSON-encoded schema
+ * @uses self::real_parse()
+ * @returns AvroSchema
+ */
+ public static function parse($json)
+ {
+ $schemata = new AvroNamedSchemata();
+ return self::real_parse(json_decode($json, true), null, $schemata);
+ }
+
+ /**
+ * @param mixed $avro JSON-decoded schema
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata reference to named schemas
+ * @returns AvroSchema
+ * @throws AvroSchemaParseException
+ */
+ static function real_parse($avro, $default_namespace=null, &$schemata=null)
+ {
+ if (is_null($schemata))
+ $schemata = new AvroNamedSchemata();
+
+ if (is_array($avro))
+ {
+ $type = AvroUtil::array_value($avro, self::TYPE_ATTR);
+
+ if (self::is_primitive_type($type))
+ return new AvroPrimitiveSchema($type);
+
+ elseif (self::is_named_type($type))
+ {
+ $name = AvroUtil::array_value($avro, self::NAME_ATTR);
+ $namespace = AvroUtil::array_value($avro, self::NAMESPACE_ATTR);
+ $new_name = new AvroName($name, $namespace, $default_namespace);
+ $doc = AvroUtil::array_value($avro, self::DOC_ATTR);
+ switch ($type)
+ {
+ case self::FIXED_SCHEMA:
+ $size = AvroUtil::array_value($avro, self::SIZE_ATTR);
+ return new AvroFixedSchema($new_name, $doc,
+ $size,
+ $schemata);
+ case self::ENUM_SCHEMA:
+ $symbols = AvroUtil::array_value($avro, self::SYMBOLS_ATTR);
+ return new AvroEnumSchema($new_name, $doc,
+ $symbols,
+ $schemata);
+ case self::RECORD_SCHEMA:
+ case self::ERROR_SCHEMA:
+ $fields = AvroUtil::array_value($avro, self::FIELDS_ATTR);
+ return new AvroRecordSchema($new_name, $doc,
+ $fields,
+ $schemata, $type);
+ default:
+ throw new AvroSchemaParseException(
+ sprintf('Unknown named type: %s', $type));
+ }
+ }
+ elseif (self::is_valid_type($type))
+ {
+ switch ($type)
+ {
+ case self::ARRAY_SCHEMA:
+ return new AvroArraySchema($avro[self::ITEMS_ATTR],
+ $default_namespace,
+ $schemata);
+ case self::MAP_SCHEMA:
+ return new AvroMapSchema($avro[self::VALUES_ATTR],
+ $default_namespace,
+ $schemata);
+ default:
+ throw new AvroSchemaParseException(
+ sprintf('Unknown valid type: %s', $type));
+ }
+ }
+ elseif (!array_key_exists(self::TYPE_ATTR, $avro)
+ && AvroUtil::is_list($avro))
+ return new AvroUnionSchema($avro, $default_namespace, $schemata);
+ else
+ throw new AvroSchemaParseException(sprintf('Undefined type: %s',
+ $type));
+ }
+ elseif (self::is_primitive_type($avro))
+ return new AvroPrimitiveSchema($avro);
+ else
+ throw new AvroSchemaParseException(
+ sprintf('%s is not a schema we know about.',
+ print_r($avro, true)));
+ }
+
+ /**
+ * @returns boolean true if $datum is valid for $expected_schema
+ * and false otherwise.
+ * @throws AvroSchemaParseException
+ */
+ public static function is_valid_datum($expected_schema, $datum)
+ {
+ switch($expected_schema->type)
+ {
+ case self::NULL_TYPE:
+ return is_null($datum);
+ case self::BOOLEAN_TYPE:
+ return is_bool($datum);
+ case self::STRING_TYPE:
+ case self::BYTES_TYPE:
+ return is_string($datum);
+ case self::INT_TYPE:
+ return (is_int($datum)
+ && (self::INT_MIN_VALUE <= $datum)
+ && ($datum <= self::INT_MAX_VALUE));
+ case self::LONG_TYPE:
+ return (is_int($datum)
+ && (self::LONG_MIN_VALUE <= $datum)
+ && ($datum <= self::LONG_MAX_VALUE));
+ case self::FLOAT_TYPE:
+ case self::DOUBLE_TYPE:
+ return (is_float($datum) || is_int($datum));
+ case self::ARRAY_SCHEMA:
+ if (is_array($datum))
+ {
+ foreach ($datum as $d)
+ if (!self::is_valid_datum($expected_schema->items(), $d))
+ return false;
+ return true;
+ }
+ return false;
+ case self::MAP_SCHEMA:
+ if (is_array($datum))
+ {
+ foreach ($datum as $k => $v)
+ if (!is_string($k)
+ || !self::is_valid_datum($expected_schema->values(), $v))
+ return false;
+ return true;
+ }
+ return false;
+ case self::UNION_SCHEMA:
+ foreach ($expected_schema->schemas() as $schema)
+ if (self::is_valid_datum($schema, $datum))
+ return true;
+ return false;
+ case self::ENUM_SCHEMA:
+ return in_array($datum, $expected_schema->symbols());
+ case self::FIXED_SCHEMA:
+ return (is_string($datum)
+ && (strlen($datum) == $expected_schema->size()));
+ case self::RECORD_SCHEMA:
+ case self::ERROR_SCHEMA:
+ case self::REQUEST_SCHEMA:
+ if (is_array($datum))
+ {
+ foreach ($expected_schema->fields() as $field)
+ if (!array_key_exists($field->name(), $datum) || !self::is_valid_datum($field->type(), $datum[$field->name()]))
+ return false;
+ return true;
+ }
+ return false;
+ default:
+ throw new AvroSchemaParseException(
+ sprintf('%s is not allowed.', $expected_schema));
+ }
+ }
+
+ /**
+ * @internal Should only be called from within the constructor of
+ * a class which extends AvroSchema
+ * @param string $type a schema type name
+ */
+ public function __construct($type)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * @param mixed $avro
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ * @returns AvroSchema
+ * @uses AvroSchema::real_parse()
+ * @throws AvroSchemaParseException
+ */
+ protected static function subparse($avro, $default_namespace, &$schemata=null)
+ {
+ try
+ {
+ return self::real_parse($avro, $default_namespace, $schemata);
+ }
+ catch (AvroSchemaParseException $e)
+ {
+ throw $e;
+ }
+ catch (Exception $e)
+ {
+ throw new AvroSchemaParseException(
+ sprintf('Sub-schema is not a valid Avro schema. Bad schema: %s',
+ print_r($avro, true)));
+ }
+
+ }
+
+ /**
+ * @returns string schema type name of this schema
+ */
+ public function type() { return $this->type; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ return array(self::TYPE_ATTR => $this->type);
+ }
+
+ /**
+ * @returns string the JSON-encoded representation of this Avro schema.
+ */
+ public function __toString() { return json_encode($this->to_avro()); }
+
+ /**
+ * @returns mixed value of the attribute with the given attribute name
+ */
+ public function attribute($attribute) { return $this->$attribute(); }
+
+}
+
+/**
+ * Avro schema for basic types such as null, int, long, string.
+ * @package Avro
+ */
+class AvroPrimitiveSchema extends AvroSchema
+{
+
+ /**
+ * @param string $type the primitive schema type name
+ * @throws AvroSchemaParseException if the given $type is not a
+ * primitive schema type name
+ */
+ public function __construct($type)
+ {
+ if (self::is_primitive_type($type))
+ return parent::__construct($type);
+ throw new AvroSchemaParseException(
+ sprintf('%s is not a valid primitive type.', $type));
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ // FIXME: Is this if really necessary? When *wouldn't* this be the case?
+ if (1 == count($avro))
+ return $this->type;
+ return $avro;
+ }
+}
+
+/**
+ * Avro array schema, consisting of items of a particular
+ * Avro schema type.
+ * @package Avro
+ */
+class AvroArraySchema extends AvroSchema
+{
+ /**
+ * @var AvroName|AvroSchema named schema name or AvroSchema of
+ * array element
+ */
+ private $items;
+
+ /**
+ * @var boolean true if the items schema
+ * FIXME: couldn't we derive this from whether or not $this->items
+ * is an AvroName or an AvroSchema?
+ */
+ private $is_items_schema_from_schemata;
+
+ /**
+ * @param string|mixed $items AvroNamedSchema name or object form
+ * of decoded JSON schema representation.
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($items, $default_namespace, &$schemata=null)
+ {
+ parent::__construct(AvroSchema::ARRAY_SCHEMA);
+
+ $this->is_items_schema_from_schemata = false;
+ $items_schema = null;
+ if (is_string($items)
+ && $items_schema = $schemata->schema_by_name(
+ new AvroName($items, null, $default_namespace)))
+ $this->is_items_schema_from_schemata = true;
+ else
+ $items_schema = AvroSchema::subparse($items, $default_namespace, $schemata);
+
+ $this->items = $items_schema;
+ }
+
+
+ /**
+ * @returns AvroName|AvroSchema named schema name or AvroSchema
+ * of this array schema's elements.
+ */
+ public function items() { return $this->items; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::ITEMS_ATTR] = $this->is_items_schema_from_schemata
+ ? $this->items->qualified_name() : $this->items->to_avro();
+ return $avro;
+ }
+}
+
+/**
+ * Avro map schema consisting of named values of defined
+ * Avro Schema types.
+ * @package Avro
+ */
+class AvroMapSchema extends AvroSchema
+{
+ /**
+ * @var string|AvroSchema named schema name or AvroSchema
+ * of map schema values.
+ */
+ private $values;
+
+ /**
+ * @var boolean true if the named schema
+ * XXX Couldn't we derive this based on whether or not
+ * $this->values is a string?
+ */
+ private $is_values_schema_from_schemata;
+
+ /**
+ * @param string|AvroSchema $values
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($values, $default_namespace, &$schemata=null)
+ {
+ parent::__construct(AvroSchema::MAP_SCHEMA);
+
+ $this->is_values_schema_from_schemata = false;
+ $values_schema = null;
+ if (is_string($values)
+ && $values_schema = $schemata->schema_by_name(
+ new AvroName($values, null, $default_namespace)))
+ $this->is_values_schema_from_schemata = true;
+ else
+ $values_schema = AvroSchema::subparse($values, $default_namespace,
+ $schemata);
+
+ $this->values = $values_schema;
+ }
+
+ /**
+ * @returns XXX|AvroSchema
+ */
+ public function values() { return $this->values; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::VALUES_ATTR] = $this->is_values_schema_from_schemata
+ ? $this->values->qualified_name() : $this->values->to_avro();
+ return $avro;
+ }
+}
+
+/**
+ * Union of Avro schemas, of which values can be of any of the schema in
+ * the union.
+ * @package Avro
+ */
+class AvroUnionSchema extends AvroSchema
+{
+ /**
+ * @var AvroSchema[] list of schemas of this union
+ */
+ private $schemas;
+
+ /**
+ * @var int[] list of indices of named schemas which
+ * are defined in $schemata
+ */
+ public $schema_from_schemata_indices;
+
+ /**
+ * @param AvroSchema[] $schemas list of schemas in the union
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($schemas, $default_namespace, &$schemata=null)
+ {
+ parent::__construct(AvroSchema::UNION_SCHEMA);
+
+ $this->schema_from_schemata_indices = array();
+ $schema_types = array();
+ foreach ($schemas as $index => $schema)
+ {
+ $is_schema_from_schemata = false;
+ $new_schema = null;
+ if (is_string($schema)
+ && ($new_schema = $schemata->schema_by_name(
+ new AvroName($schema, null, $default_namespace))))
+ $is_schema_from_schemata = true;
+ else
+ $new_schema = self::subparse($schema, $default_namespace, $schemata);
+
+ $schema_type = $new_schema->type;
+ if (self::is_valid_type($schema_type)
+ && !self::is_named_type($schema_type)
+ && in_array($schema_type, $schema_types))
+ throw new AvroSchemaParseException(
+ sprintf('"%s" is already in union', $schema_type));
+ elseif (AvroSchema::UNION_SCHEMA == $schema_type)
+ throw new AvroSchemaParseException('Unions cannot contain other unions');
+ else
+ {
+ $schema_types []= $schema_type;
+ $this->schemas []= $new_schema;
+ if ($is_schema_from_schemata)
+ $this->schema_from_schemata_indices []= $index;
+ }
+ }
+
+ }
+
+ /**
+ * @returns AvroSchema[]
+ */
+ public function schemas() { return $this->schemas; }
+
+ /**
+ * @returns AvroSchema the particular schema from the union for
+ * the given (zero-based) index.
+ * @throws AvroSchemaParseException if the index is invalid for this schema.
+ */
+ public function schema_by_index($index)
+ {
+ if (count($this->schemas) > $index)
+ return $this->schemas[$index];
+
+ throw new AvroSchemaParseException('Invalid union schema index');
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = array();
+
+ foreach ($this->schemas as $index => $schema)
+ $avro []= (in_array($index, $this->schema_from_schemata_indices))
+ ? $schema->qualified_name() : $schema->to_avro();
+
+ return $avro;
+ }
+}
+
+/**
+ * Parent class of named Avro schema
+ * @package Avro
+ * @todo Refactor AvroNamedSchema to use an AvroName instance
+ * to store name information.
+ */
+class AvroNamedSchema extends AvroSchema
+{
+ /**
+ * @var AvroName $name
+ */
+ private $name;
+
+ /**
+ * @var string documentation string
+ */
+ private $doc;
+
+ /**
+ * @param string $type
+ * @param AvroName $name
+ * @param string $doc documentation string
+ * @param AvroNamedSchemata &$schemata
+ * @throws AvroSchemaParseException
+ */
+ public function __construct($type, $name, $doc=null, &$schemata=null)
+ {
+ parent::__construct($type);
+ $this->name = $name;
+
+ if ($doc && !is_string($doc))
+ throw new AvroSchemaParseException('Schema doc attribute must be a string');
+ $this->doc = $doc;
+
+ if (!is_null($schemata))
+ $schemata = $schemata->clone_with_new_schema($this);
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ list($name, $namespace) = AvroName::extract_namespace($this->qualified_name());
+ $avro[AvroSchema::NAME_ATTR] = $name;
+ if ($namespace)
+ $avro[AvroSchema::NAMESPACE_ATTR] = $namespace;
+ if (!is_null($this->doc))
+ $avro[AvroSchema::DOC_ATTR] = $this->doc;
+ return $avro;
+ }
+
+ /**
+ * @returns string
+ */
+ public function fullname() { return $this->name->fullname(); }
+
+ public function qualified_name() { return $this->name->qualified_name(); }
+
+}
+
+/**
+ * @package Avro
+ */
+class AvroName
+{
+ /**
+ * @var string character used to separate names comprising the fullname
+ */
+ const NAME_SEPARATOR = '.';
+
+ /**
+ * @var string regular expression to validate name values
+ */
+ const NAME_REGEXP = '/^[A-Za-z_][A-Za-z0-9_]*$/';
+
+ /**
+ * @returns string[] array($name, $namespace)
+ */
+ public static function extract_namespace($name, $namespace=null)
+ {
+ $parts = explode(self::NAME_SEPARATOR, $name);
+ if (count($parts) > 1)
+ {
+ $name = array_pop($parts);
+ $namespace = join(self::NAME_SEPARATOR, $parts);
+ }
+ return array($name, $namespace);
+ }
+
+ /**
+ * @returns boolean true if the given name is well-formed
+ * (is a non-null, non-empty string) and false otherwise
+ */
+ public static function is_well_formed_name($name)
+ {
+ return (is_string($name) && !empty($name)
+ && preg_match(self::NAME_REGEXP, $name));
+ }
+
+ /**
+ * @param string $namespace
+ * @returns boolean true if namespace is composed of valid names
+ * @throws AvroSchemaParseException if any of the namespace components
+ * are invalid.
+ */
+ private static function check_namespace_names($namespace)
+ {
+ foreach (explode(self::NAME_SEPARATOR, $namespace) as $n)
+ {
+ if (empty($n) || (0 == preg_match(self::NAME_REGEXP, $n)))
+ throw new AvroSchemaParseException(sprintf('Invalid name "%s"', $n));
+ }
+ return true;
+ }
+
+ /**
+ * @param string $name
+ * @param string $namespace
+ * @returns string
+ * @throws AvroSchemaParseException if any of the names are not valid.
+ */
+ private static function parse_fullname($name, $namespace)
+ {
+ if (!is_string($namespace) || empty($namespace))
+ throw new AvroSchemaParseException('Namespace must be a non-empty string.');
+ self::check_namespace_names($namespace);
+ return $namespace . '.' . $name;
+ }
+
+ /**
+ * @var string valid names are matched by self::NAME_REGEXP
+ */
+ private $name;
+
+ /**
+ * @var string
+ */
+ private $namespace;
+
+ /**
+ * @var string
+ */
+ private $fullname;
+
+ /**
+ * @var string Name qualified as necessary given its default namespace.
+ */
+ private $qualified_name;
+
+ /**
+ * @param string $name
+ * @param string $namespace
+ * @param string $default_namespace
+ */
+ public function __construct($name, $namespace, $default_namespace)
+ {
+ if (!is_string($name) || empty($name))
+ throw new AvroSchemaParseException('Name must be a non-empty string.');
+
+ if (strpos($name, self::NAME_SEPARATOR)
+ && self::check_namespace_names($name))
+ $this->fullname = $name;
+ elseif (0 == preg_match(self::NAME_REGEXP, $name))
+ throw new AvroSchemaParseException(sprintf('Invalid name "%s"', $name));
+ elseif (!is_null($namespace))
+ $this->fullname = self::parse_fullname($name, $namespace);
+ elseif (!is_null($default_namespace))
+ $this->fullname = self::parse_fullname($name, $default_namespace);
+ else
+ $this->fullname = $name;
+
+ list($this->name, $this->namespace) = self::extract_namespace($this->fullname);
+ $this->qualified_name = (is_null($this->namespace)
+ || $this->namespace == $default_namespace)
+ ? $this->name : $this->fullname;
+ }
+
+ /**
+ * @returns array array($name, $namespace)
+ */
+ public function name_and_namespace()
+ {
+ return array($this->name, $this->namespace);
+ }
+
+ /**
+ * @returns string
+ */
+ public function fullname() { return $this->fullname; }
+
+ /**
+ * @returns string fullname
+ * @uses $this->fullname()
+ */
+ public function __toString() { return $this->fullname(); }
+
+ /**
+ * @returns string name qualified for its context
+ */
+ public function qualified_name() { return $this->qualified_name; }
+
+}
+
+/**
+ * Keeps track of AvroNamedSchema which have been observed so far,
+ * as well as the default namespace.
+ *
+ * @package Avro
+ */
+class AvroNamedSchemata
+{
+ /**
+ * @var AvroNamedSchema[]
+ */
+ private $schemata;
+
+ /**
+ * @param AvroNamedSchemata[]
+ */
+ public function __construct($schemata=array())
+ {
+ $this->schemata = $schemata;
+ }
+
+ public function list_schemas() {
+ var_export($this->schemata);
+ foreach($this->schemata as $sch)
+ print('Schema '.$sch->__toString()."\n");
+ }
+
+ /**
+ * @param string $fullname
+ * @returns boolean true if there exists a schema with the given name
+ * and false otherwise.
+ */
+ public function has_name($fullname)
+ {
+ return array_key_exists($fullname, $this->schemata);
+ }
+
+ /**
+ * @param string $fullname
+ * @returns AvroSchema|null the schema which has the given name,
+ * or null if there is no schema with the given name.
+ */
+ public function schema($fullname)
+ {
+ if (isset($this->schemata[$fullname]))
+ return $this->schemata[$fullname];
+ return null;
+ }
+
+ /**
+ * @param AvroName $name
+ * @returns AvroSchema|null
+ */
+ public function schema_by_name($name)
+ {
+ return $this->schema($name->fullname());
+ }
+
+ /**
+ * Creates a new AvroNamedSchemata instance of this schemata instance
+ * with the given $schema appended.
+ * @param AvroNamedSchema schema to add to this existing schemata
+ * @returns AvroNamedSchemata
+ */
+ public function clone_with_new_schema($schema)
+ {
+ $name = $schema->fullname();
+ if (AvroSchema::is_valid_type($name))
+ throw new AvroSchemaParseException(
+ sprintf('Name "%s" is a reserved type name', $name));
+ else if ($this->has_name($name))
+ throw new AvroSchemaParseException(
+ sprintf('Name "%s" is already in use', $name));
+ $schemata = new AvroNamedSchemata($this->schemata);
+ $schemata->schemata[$name] = $schema;
+ return $schemata;
+ }
+}
+
+/**
+ * @package Avro
+ */
+class AvroEnumSchema extends AvroNamedSchema
+{
+ /**
+ * @var string[] array of symbols
+ */
+ private $symbols;
+
+ /**
+ * @param AvroName $name
+ * @param string $doc
+ * @param string[] $symbols
+ * @param AvroNamedSchemata &$schemata
+ * @throws AvroSchemaParseException
+ */
+ public function __construct($name, $doc, $symbols, &$schemata=null)
+ {
+ if (!AvroUtil::is_list($symbols))
+ throw new AvroSchemaParseException('Enum Schema symbols are not a list');
+
+ if (count(array_unique($symbols)) > count($symbols))
+ throw new AvroSchemaParseException(
+ sprintf('Duplicate symbols: %s', $symbols));
+
+ foreach ($symbols as $symbol)
+ if (!is_string($symbol) || empty($symbol))
+ throw new AvroSchemaParseException(
+ sprintf('Enum schema symbol must be a string %',
+ print_r($symbol, true)));
+
+ parent::__construct(AvroSchema::ENUM_SCHEMA, $name, $doc, $schemata);
+ $this->symbols = $symbols;
+ }
+
+ /**
+ * @returns string[] this enum schema's symbols
+ */
+ public function symbols() { return $this->symbols; }
+
+ /**
+ * @param string $symbol
+ * @returns boolean true if the given symbol exists in this
+ * enum schema and false otherwise
+ */
+ public function has_symbol($symbol)
+ {
+ return in_array($symbol, $this->symbols);
+ }
+
+ /**
+ * @param int $index
+ * @returns string enum schema symbol with the given (zero-based) index
+ */
+ public function symbol_by_index($index)
+ {
+ if (array_key_exists($index, $this->symbols))
+ return $this->symbols[$index];
+ throw new AvroException(sprintf('Invalid symbol index %d', $index));
+ }
+
+ /**
+ * @param string $symbol
+ * @returns int the index of the given $symbol in the enum schema
+ */
+ public function symbol_index($symbol)
+ {
+ $idx = array_search($symbol, $this->symbols, true);
+ if (false !== $idx)
+ return $idx;
+ throw new AvroException(sprintf("Invalid symbol value '%s'", $symbol));
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::SYMBOLS_ATTR] = $this->symbols;
+ return $avro;
+ }
+}
+
+/**
+ * AvroNamedSchema with fixed-length data values
+ * @package Avro
+ */
+class AvroFixedSchema extends AvroNamedSchema
+{
+
+ /**
+ * @var int byte count of this fixed schema data value
+ */
+ private $size;
+
+ /**
+ * @param AvroName $name
+ * @param string $doc Set to null, as fixed schemas don't have doc strings
+ * @param int $size byte count of this fixed schema data value
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($name, $doc, $size, &$schemata=null)
+ {
+ $doc = null; // Fixed schemas don't have doc strings.
+ if (!is_integer($size))
+ throw new AvroSchemaParseException(
+ 'Fixed Schema requires a valid integer for "size" attribute');
+ parent::__construct(AvroSchema::FIXED_SCHEMA, $name, $doc, $schemata);
+ return $this->size = $size;
+ }
+
+ /**
+ * @returns int byte count of this fixed schema data value
+ */
+ public function size() { return $this->size; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::SIZE_ATTR] = $this->size;
+ return $avro;
+ }
+}
+
+/**
+ * @package Avro
+ */
+class AvroRecordSchema extends AvroNamedSchema
+{
+ /**
+ * @param mixed $field_data
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ * @returns AvroField[]
+ * @throws AvroSchemaParseException
+ */
+ static function parse_fields($field_data, $default_namespace, &$schemata)
+ {
+ $fields = array();
+ $field_names = array();
+ foreach ($field_data as $index => $field)
+ {
+ $name = AvroUtil::array_value($field, AvroField::FIELD_NAME_ATTR);
+ $type = AvroUtil::array_value($field, AvroSchema::TYPE_ATTR);
+ $order = AvroUtil::array_value($field, AvroField::ORDER_ATTR);
+
+ $default = null;
+ $has_default = false;
+ if (array_key_exists(AvroField::DEFAULT_ATTR, $field))
+ {
+ $default = $field[AvroField::DEFAULT_ATTR];
+ $has_default = true;
+ }
+
+ if (in_array($name, $field_names))
+ throw new AvroSchemaParseException(
+ sprintf("Field name %s is already in use", $name));
+
+ $is_schema_from_schemata = false;
+ $field_schema = null;
+ if (is_string($type)
+ && $field_schema = $schemata->schema_by_name(
+ new AvroName($type, null, $default_namespace)))
+ $is_schema_from_schemata = true;
+ else
+ $field_schema = self::subparse($type, $default_namespace, $schemata);
+
+ $new_field = new AvroField($name, $field_schema, $is_schema_from_schemata,
+ $has_default, $default, $order);
+ $field_names []= $name;
+ $fields []= $new_field;
+ }
+ return $fields;
+ }
+
+ /**
+ * @var AvroSchema[] array of AvroNamedSchema field definitions of
+ * this AvroRecordSchema
+ */
+ private $fields;
+
+ /**
+ * @var array map of field names to field objects.
+ * @internal Not called directly. Memoization of AvroRecordSchema->fields_hash()
+ */
+ private $fields_hash;
+
+ /**
+ * @param string $name
+ * @param string $namespace
+ * @param string $doc
+ * @param array $fields
+ * @param AvroNamedSchemata &$schemata
+ * @param string $schema_type schema type name
+ * @throws AvroSchemaParseException
+ */
+ public function __construct($name, $doc, $fields, &$schemata=null,
+ $schema_type=AvroSchema::RECORD_SCHEMA)
+ {
+ if (is_null($fields))
+ throw new AvroSchemaParseException(
+ 'Record schema requires a non-empty fields attribute');
+
+ if (AvroSchema::REQUEST_SCHEMA == $schema_type)
+ parent::__construct($schema_type, $name);
+ else
+ parent::__construct($schema_type, $name, $doc, $schemata);
+
+ list($x, $namespace) = $name->name_and_namespace();
+ $this->fields = self::parse_fields($fields, $namespace, $schemata);
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+
+ $fields_avro = array();
+ foreach ($this->fields as $field)
+ $fields_avro [] = $field->to_avro();
+
+ if (AvroSchema::REQUEST_SCHEMA == $this->type)
+ return $fields_avro;
+
+ $avro[AvroSchema::FIELDS_ATTR] = $fields_avro;
+
+ return $avro;
+ }
+
+ /**
+ * @returns array the schema definitions of the fields of this AvroRecordSchema
+ */
+ public function fields() { return $this->fields; }
+
+ /**
+ * @returns array a hash table of the fields of this AvroRecordSchema fields
+ * keyed by each field's name
+ */
+ public function fields_hash()
+ {
+ if (is_null($this->fields_hash))
+ {
+ $hash = array();
+ foreach ($this->fields as $field)
+ $hash[$field->name()] = $field;
+ $this->fields_hash = $hash;
+ }
+ return $this->fields_hash;
+ }
+}
+
+/**
+ * Field of an {@link AvroRecordSchema}
+ * @package Avro
+ */
+class AvroField extends AvroSchema
+{
+
+ /**
+ * @var string fields name attribute name
+ */
+ const FIELD_NAME_ATTR = 'name';
+
+ /**
+ * @var string
+ */
+ const DEFAULT_ATTR = 'default';
+
+ /**
+ * @var string
+ */
+ const ORDER_ATTR = 'order';
+
+ /**
+ * @var string
+ */
+ const ASC_SORT_ORDER = 'ascending';
+
+ /**
+ * @var string
+ */
+ const DESC_SORT_ORDER = 'descending';
+
+ /**
+ * @var string
+ */
+ const IGNORE_SORT_ORDER = 'ignore';
+
+ /**
+ * @var array list of valid field sort order values
+ */
+ private static $valid_field_sort_orders = array(self::ASC_SORT_ORDER,
+ self::DESC_SORT_ORDER,
+ self::IGNORE_SORT_ORDER);
+
+
+ /**
+ * @param string $order
+ * @returns boolean
+ */
+ private static function is_valid_field_sort_order($order)
+ {
+ return in_array($order, self::$valid_field_sort_orders);
+ }
+
+ /**
+ * @param string $order
+ * @throws AvroSchemaParseException if $order is not a valid
+ * field order value.
+ */
+ private static function check_order_value($order)
+ {
+ if (!is_null($order) && !self::is_valid_field_sort_order($order))
+ throw new AvroSchemaParseException(
+ sprintf('Invalid field sort order %s', $order));
+ }
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var boolean whether or no there is a default value
+ */
+ private $has_default;
+
+ /**
+ * @var string field default value
+ */
+ private $default;
+
+ /**
+ * @var string sort order of this field
+ */
+ private $order;
+
+ /**
+ * @var boolean whether or not the AvroNamedSchema of this field is
+ * defined in the AvroNamedSchemata instance
+ */
+ private $is_type_from_schemata;
+
+ /**
+ * @param string $type
+ * @param string $name
+ * @param AvroSchema $schema
+ * @param boolean $is_type_from_schemata
+ * @param string $default
+ * @param string $order
+ * @todo Check validity of $default value
+ * @todo Check validity of $order value
+ */
+ public function __construct($name, $schema, $is_type_from_schemata,
+ $has_default, $default, $order=null)
+ {
+ if (!AvroName::is_well_formed_name($name))
+ throw new AvroSchemaParseException('Field requires a "name" attribute');
+
+ $this->type = $schema;
+ $this->is_type_from_schemata = $is_type_from_schemata;
+ $this->name = $name;
+ $this->has_default = $has_default;
+ if ($this->has_default)
+ $this->default = $default;
+ $this->check_order_value($order);
+ $this->order = $order;
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = array(AvroField::FIELD_NAME_ATTR => $this->name);
+
+ $avro[AvroSchema::TYPE_ATTR] = ($this->is_type_from_schemata)
+ ? $this->type->qualified_name() : $this->type->to_avro();
+
+ if (isset($this->default))
+ $avro[AvroField::DEFAULT_ATTR] = $this->default;
+
+ if ($this->order)
+ $avro[AvroField::ORDER_ATTR] = $this->order;
+
+ return $avro;
+ }
+
+ /**
+ * @returns string the name of this field
+ */
+ public function name() { return $this->name; }
+
+ /**
+ * @returns mixed the default value of this field
+ */
+ public function default_value() { return $this->default; }
+
+ /**
+ * @returns boolean true if the field has a default and false otherwise
+ */
+ public function has_default_value() { return $this->has_default; }
+}
diff --git a/vendor/wikimedia/avro/lib/avro/util.php b/vendor/wikimedia/avro/lib/avro/util.php
new file mode 100644
index 00000000..a43613e9
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/util.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Class for static utility methods used in Avro.
+ *
+ * @package Avro
+ */
+class AvroUtil
+{
+ /**
+ * Determines whether the given array is an associative array
+ * (what is termed a map, hash, or dictionary in other languages)
+ * or a list (an array with monotonically increasing integer indicies
+ * starting with zero).
+ *
+ * @param array $ary array to test
+ * @returns true if the array is a list and false otherwise.
+ *
+ */
+ static function is_list($ary)
+ {
+ if (is_array($ary))
+ {
+ $i = 0;
+ foreach ($ary as $k => $v)
+ {
+ if ($i !== $k)
+ return false;
+ $i++;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param array $ary
+ * @param string $key
+ * @returns mixed the value of $ary[$key] if it is set,
+ * and null otherwise.
+ */
+ static function array_value($ary, $key)
+ {
+ return isset($ary[$key]) ? $ary[$key] : null;
+ }
+}