Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/third_party/google/LICENSE b/third_party/google/LICENSE
new file mode 100644
index 0000000..53d6fcb
--- /dev/null
+++ b/third_party/google/LICENSE
@@ -0,0 +1,132 @@
+GOOGLE APP ENGINE SDK
+=====================
+Copyright 2008 Google Inc.
+All rights reserved.
+
+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.
+
+
+DJANGO FRAMEWORK
+================
+Copyright (c) 2005, the Lawrence Journal-World
+All rights reserved.
+
+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. Neither the name of Django nor the names of its contributors may be used
+       to endorse or promote products derived from this software without
+       specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+WebOb
+======
+
+Copyright (c) 2007 Ian Bicking and Contributors
+
+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.
+
+
+PyYaml
+=======
+Copyright (c) 2006 Kirill Simonov
+
+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.
+
+
+cacerts
+=======
+Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+The contents of this file are subject to the Mozilla Public License Version
+1.1 (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.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+for the specific language governing rights and limitations under the
+License.
+
+The Original Code is the Netscape security libraries.
+
+The Initial Developer of the Original Code is
+Netscape Communications Corporation.
+Portions created by the Initial Developer are Copyright (C) 1994-2000
+the Initial Developer. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms of
+either the GNU General Public License Version 2 or later (the "GPL"), or
+the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+in which case the provisions of the GPL or the LGPL are applicable instead
+of those above. If you wish to allow use of your version of this file only
+under the terms of either the GPL or the LGPL, and not to allow others to
+use your version of this file under the terms of the MPL, indicate your
+decision by deleting the provisions above and replace them with the notice
+and other provisions required by the GPL or the LGPL. If you do not delete
+the provisions above, a recipient may use your version of this file under
+the terms of any one of the MPL, the GPL or the LGPL.
+
diff --git a/third_party/google/README.monorail b/third_party/google/README.monorail
new file mode 100644
index 0000000..c9043a3
--- /dev/null
+++ b/third_party/google/README.monorail
@@ -0,0 +1,35 @@
+Name: Google App Engine SDK
+URL: https://github.com/GoogleCloudPlatform/appengine-python-standard
+Version: May 18, 2022
+License: Apache 2.0
+License File: LICENSE
+Security Critical: no
+Description:
+Development tools for Google App Engine
+Local Modifications:
+While most App Engine APIs have been updated for Python 3 in the above GitHub
+repository, ProtocolBuffer is not available. Therefore, we have copied the file
+from the old Python 2 API and are updating it ourselves for Python 3.
+
+1. Install the Google Cloud SDK (https://cloud.google.com/sdk)
+   The App Engine Python SDK is located in gcloud/platform/google_appengine/
+2. Retain only:
+   google/net/__init__.py
+   google/net/proto/__init__.py
+   google/net/proto/ProtocolBuffer.py
+   LICENSE
+3. Strip trailing whitespace from all files.
+4. Update files for Python 3.
+   Syntax changes:
+   * raise Exception, s --> raise Exception(s)
+
+   Import moves:
+   * import httplib --> from six.moves import http_client
+
+   String changes:
+   * a.fromstring(s) --> a.frombytes(six.ensure_binary(s))
+   * a.tostring() --> a.tobytes()
+
+   Integer changes:
+   * 1234L --> 1234
+   * long(1234) --> 1234
diff --git a/third_party/google/net/__init__.py b/third_party/google/net/__init__.py
new file mode 100644
index 0000000..93e6786
--- /dev/null
+++ b/third_party/google/net/__init__.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google LLC
+#
+# 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.
+#
diff --git a/third_party/google/net/proto/ProtocolBuffer.py b/third_party/google/net/proto/ProtocolBuffer.py
new file mode 100644
index 0000000..a45183d
--- /dev/null
+++ b/third_party/google/net/proto/ProtocolBuffer.py
@@ -0,0 +1,1203 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google LLC
+#
+# 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.
+#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+import array
+import itertools
+import re
+import six
+from six.moves import http_client
+import struct
+try:
+
+
+  import google.net.proto.proto1 as proto1
+except ImportError:
+
+  class ProtocolBufferDecodeError(Exception): pass
+  class ProtocolBufferEncodeError(Exception): pass
+  class ProtocolBufferReturnError(Exception): pass
+else:
+  ProtocolBufferDecodeError = proto1.ProtocolBufferDecodeError
+  ProtocolBufferEncodeError = proto1.ProtocolBufferEncodeError
+  ProtocolBufferReturnError = proto1.ProtocolBufferReturnError
+
+__all__ = ['ProtocolMessage', 'Encoder', 'Decoder',
+           'ExtendableProtocolMessage',
+           'ProtocolBufferDecodeError',
+           'ProtocolBufferEncodeError',
+           'ProtocolBufferReturnError']
+
+URL_RE = re.compile('^(https?)://([^/]+)(/.*)$')
+
+
+class ProtocolMessage:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  def __init__(self, contents=None):
+
+
+    raise NotImplementedError
+
+  def Clear(self):
+
+
+    raise NotImplementedError
+
+  def IsInitialized(self, debug_strs=None):
+
+    raise NotImplementedError
+
+  def Encode(self):
+
+    try:
+      return self._CEncode()
+    except (NotImplementedError, AttributeError):
+      e = Encoder()
+      self.Output(e)
+      return e.buffer().tobytes()
+
+  def SerializeToString(self):
+
+    return self.Encode()
+
+  def SerializePartialToString(self):
+
+
+
+    try:
+      return self._CEncodePartial()
+    except (NotImplementedError, AttributeError):
+      e = Encoder()
+      self.OutputPartial(e)
+      return e.buffer().tobytes()
+
+  def _CEncode(self):
+
+
+
+
+
+
+
+    raise NotImplementedError
+
+  def _CEncodePartial(self):
+
+    raise NotImplementedError
+
+  def ParseFromString(self, s):
+
+
+
+    self.Clear()
+    self.MergeFromString(s)
+
+  def ParsePartialFromString(self, s):
+
+
+    self.Clear()
+    self.MergePartialFromString(s)
+
+  def MergeFromString(self, s):
+
+
+
+    self.MergePartialFromString(s)
+    dbg = []
+    if not self.IsInitialized(dbg):
+      raise ProtocolBufferDecodeError('\n\t'.join(dbg))
+
+  def MergePartialFromString(self, s):
+
+
+    try:
+      self._CMergeFromString(s)
+    except (NotImplementedError, AttributeError):
+
+
+      a = array.array('B')
+      a.frombytes(six.ensure_binary(s))
+      d = Decoder(a, 0, len(a))
+      self.TryMerge(d)
+
+  def _CMergeFromString(self, s):
+
+
+
+
+
+
+
+
+
+    raise NotImplementedError
+
+  def __getstate__(self):
+
+
+    return self.Encode()
+
+  def __setstate__(self, contents_):
+
+
+    self.__init__(contents=contents_)
+
+  def sendCommand(self, server, url, response, follow_redirects=1,
+                  secure=0, keyfile=None, certfile=None):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    data = self.Encode()
+    if secure:
+      if keyfile and certfile:
+        conn = http_client.HTTPSConnection(server, key_file=keyfile,
+                                       cert_file=certfile)
+      else:
+        conn = http_client.HTTPSConnection(server)
+    else:
+      conn = http_client.HTTPConnection(server)
+    conn.putrequest("POST", url)
+    conn.putheader("Content-Length", "%d" %len(data))
+    conn.endheaders()
+    conn.send(data)
+    resp = conn.getresponse()
+    if follow_redirects > 0 and resp.status == 302:
+      m = URL_RE.match(resp.getheader('Location'))
+      if m:
+        protocol, server, url = m.groups()
+        return self.sendCommand(server, url, response,
+                                follow_redirects=follow_redirects - 1,
+                                secure=(protocol == 'https'),
+                                keyfile=keyfile,
+                                certfile=certfile)
+    if resp.status != 200:
+      raise ProtocolBufferReturnError(resp.status)
+    if response is not None:
+      response.ParseFromString(resp.read())
+    return response
+
+  def sendSecureCommand(self, server, keyfile, certfile, url, response,
+                        follow_redirects=1):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    return self.sendCommand(server, url, response,
+                            follow_redirects=follow_redirects,
+                            secure=1, keyfile=keyfile, certfile=certfile)
+
+  def __str__(self, prefix="", printElemNumber=0):
+
+    raise NotImplementedError
+
+  def ToASCII(self):
+
+    return self._CToASCII(ProtocolMessage._SYMBOLIC_FULL_ASCII)
+
+  def ToShortASCII(self):
+
+
+
+
+    return self._CToASCII(ProtocolMessage._SYMBOLIC_SHORT_ASCII)
+
+
+
+  _NUMERIC_ASCII = 0
+  _SYMBOLIC_SHORT_ASCII = 1
+  _SYMBOLIC_FULL_ASCII = 2
+
+  def _CToASCII(self, output_format):
+
+
+
+
+
+    raise NotImplementedError
+
+  def ParseASCII(self, ascii_string):
+
+
+
+
+    raise NotImplementedError
+
+  def ParseASCIIIgnoreUnknown(self, ascii_string):
+
+
+
+
+    raise NotImplementedError
+
+  def Equals(self, other):
+
+
+
+
+    raise NotImplementedError
+
+  def __eq__(self, other):
+
+
+
+
+
+
+    if other.__class__ is self.__class__:
+      return self.Equals(other)
+    return NotImplemented
+
+  def __ne__(self, other):
+
+
+
+
+
+
+    if other.__class__ is self.__class__:
+      return not self.Equals(other)
+    return NotImplemented
+
+
+
+
+
+  def Output(self, e):
+
+    dbg = []
+    if not self.IsInitialized(dbg):
+      raise ProtocolBufferEncodeError('\n\t'.join(dbg))
+    self.OutputUnchecked(e)
+    return
+
+  def OutputUnchecked(self, e):
+
+    raise NotImplementedError
+
+  def OutputPartial(self, e):
+
+
+    raise NotImplementedError
+
+  def Parse(self, d):
+
+    self.Clear()
+    self.Merge(d)
+    return
+
+  def Merge(self, d):
+
+    self.TryMerge(d)
+    dbg = []
+    if not self.IsInitialized(dbg):
+      raise ProtocolBufferDecodeError('\n\t'.join(dbg))
+    return
+
+  def TryMerge(self, d):
+
+    raise NotImplementedError
+
+  def CopyFrom(self, pb):
+
+    if (pb == self): return
+    self.Clear()
+    self.MergeFrom(pb)
+
+  def MergeFrom(self, pb):
+
+    raise NotImplementedError
+
+
+
+
+
+  def lengthVarInt32(self, n):
+    return self.lengthVarInt64(n)
+
+  def lengthVarInt64(self, n):
+    if n < 0:
+      return 10
+    result = 0
+    while 1:
+      result += 1
+      n >>= 7
+      if n == 0:
+        break
+    return result
+
+  def lengthString(self, n):
+    return self.lengthVarInt32(n) + n
+
+  def DebugFormat(self, value):
+    return "%s" % value
+  def DebugFormatInt32(self, value):
+    if (value <= -2000000000 or value >= 2000000000):
+      return self.DebugFormatFixed32(value)
+    return "%d" % value
+  def DebugFormatInt64(self, value):
+    if (value <= -20000000000000 or value >= 20000000000000):
+      return self.DebugFormatFixed64(value)
+    return "%d" % value
+  def DebugFormatString(self, value):
+
+
+
+    def escape(c):
+      o = ord(c)
+      if o == 10: return r"\n"
+      if o == 39: return r"\'"
+
+      if o == 34: return r'\"'
+      if o == 92: return r"\\"
+
+      if o >= 127 or o < 32: return "\\%03o" % o
+      return c
+    return '"' + "".join(escape(c) for c in value) + '"'
+  def DebugFormatFloat(self, value):
+    return "%ff" % value
+  def DebugFormatFixed32(self, value):
+    if (value < 0): value += (1<<32)
+    return "0x%x" % value
+  def DebugFormatFixed64(self, value):
+    if (value < 0): value += (1<<64)
+    return "0x%x" % value
+  def DebugFormatBool(self, value):
+    if value:
+      return "true"
+    else:
+      return "false"
+
+
+TYPE_DOUBLE  = 1
+TYPE_FLOAT   = 2
+TYPE_INT64   = 3
+TYPE_UINT64  = 4
+TYPE_INT32   = 5
+TYPE_FIXED64 = 6
+TYPE_FIXED32 = 7
+TYPE_BOOL    = 8
+TYPE_STRING  = 9
+TYPE_GROUP   = 10
+TYPE_FOREIGN = 11
+
+
+_TYPE_TO_DEBUG_STRING = {
+    TYPE_INT32:   ProtocolMessage.DebugFormatInt32,
+    TYPE_INT64:   ProtocolMessage.DebugFormatInt64,
+    TYPE_UINT64:  ProtocolMessage.DebugFormatInt64,
+    TYPE_FLOAT:   ProtocolMessage.DebugFormatFloat,
+    TYPE_STRING:  ProtocolMessage.DebugFormatString,
+    TYPE_FIXED32: ProtocolMessage.DebugFormatFixed32,
+    TYPE_FIXED64: ProtocolMessage.DebugFormatFixed64,
+    TYPE_BOOL:    ProtocolMessage.DebugFormatBool }
+
+
+
+class Encoder:
+
+
+  NUMERIC     = 0
+  DOUBLE      = 1
+  STRING      = 2
+  STARTGROUP  = 3
+  ENDGROUP    = 4
+  FLOAT       = 5
+  MAX_TYPE    = 6
+
+  def __init__(self):
+    self.buf = array.array('B')
+    return
+
+  def buffer(self):
+    return self.buf
+
+  def put8(self, v):
+    if v < 0 or v >= (1<<8): raise ProtocolBufferEncodeError("u8 too big")
+    self.buf.append(v & 255)
+    return
+
+  def put16(self, v):
+    if v < 0 or v >= (1<<16): raise ProtocolBufferEncodeError("u16 too big")
+    self.buf.append((v >> 0) & 255)
+    self.buf.append((v >> 8) & 255)
+    return
+
+  def put32(self, v):
+    if v < 0 or v >= (1<<32): raise ProtocolBufferEncodeError("u32 too big")
+    self.buf.append((v >> 0) & 255)
+    self.buf.append((v >> 8) & 255)
+    self.buf.append((v >> 16) & 255)
+    self.buf.append((v >> 24) & 255)
+    return
+
+  def put64(self, v):
+    if v < 0 or v >= (1<<64): raise ProtocolBufferEncodeError("u64 too big")
+    self.buf.append((v >> 0) & 255)
+    self.buf.append((v >> 8) & 255)
+    self.buf.append((v >> 16) & 255)
+    self.buf.append((v >> 24) & 255)
+    self.buf.append((v >> 32) & 255)
+    self.buf.append((v >> 40) & 255)
+    self.buf.append((v >> 48) & 255)
+    self.buf.append((v >> 56) & 255)
+    return
+
+  def putVarInt32(self, v):
+
+
+
+
+
+
+
+
+    buf_append = self.buf.append
+    if v & 127 == v:
+      buf_append(v)
+      return
+    if v >= 0x80000000 or v < -0x80000000:
+      raise ProtocolBufferEncodeError("int32 too big")
+    if v < 0:
+      v += 0x10000000000000000
+    while True:
+      bits = v & 127
+      v >>= 7
+      if v:
+        bits |= 128
+      buf_append(bits)
+      if not v:
+        break
+    return
+
+  def putVarInt64(self, v):
+    buf_append = self.buf.append
+    if v >= 0x8000000000000000 or v < -0x8000000000000000:
+      raise ProtocolBufferEncodeError("int64 too big")
+    if v < 0:
+      v += 0x10000000000000000
+    while True:
+      bits = v & 127
+      v >>= 7
+      if v:
+        bits |= 128
+      buf_append(bits)
+      if not v:
+        break
+    return
+
+  def putVarUint64(self, v):
+    buf_append = self.buf.append
+    if v < 0 or v >= 0x10000000000000000:
+      raise ProtocolBufferEncodeError("uint64 too big")
+    while True:
+      bits = v & 127
+      v >>= 7
+      if v:
+        bits |= 128
+      buf_append(bits)
+      if not v:
+        break
+    return
+
+  def putFloat(self, v):
+    a = array.array('B')
+    a.frombytes(struct.pack("<f", v))
+    self.buf.extend(a)
+    return
+
+  def putDouble(self, v):
+    a = array.array('B')
+    a.frombytes(struct.pack("<d", v))
+    self.buf.extend(a)
+    return
+
+  def putBoolean(self, v):
+    if v:
+      self.buf.append(1)
+    else:
+      self.buf.append(0)
+    return
+
+  def putPrefixedString(self, v):
+
+
+
+    v = six.ensure_binary(v)
+    self.putVarInt32(len(v))
+    self.buf.frombytes(v)
+
+  def putRawString(self, v):
+    self.buf.frombytes(six.ensure_binary(v))
+
+  _TYPE_TO_METHOD = {
+      TYPE_DOUBLE:   putDouble,
+      TYPE_FLOAT:    putFloat,
+      TYPE_FIXED64:  put64,
+      TYPE_FIXED32:  put32,
+      TYPE_INT32:    putVarInt32,
+      TYPE_INT64:    putVarInt64,
+      TYPE_UINT64:   putVarUint64,
+      TYPE_BOOL:     putBoolean,
+      TYPE_STRING:   putPrefixedString }
+
+  _TYPE_TO_BYTE_SIZE = {
+      TYPE_DOUBLE:  8,
+      TYPE_FLOAT:   4,
+      TYPE_FIXED64: 8,
+      TYPE_FIXED32: 4,
+      TYPE_BOOL:    1 }
+
+class Decoder:
+  def __init__(self, buf, idx, limit):
+    self.buf = buf
+    self.idx = idx
+    self.limit = limit
+    return
+
+  def avail(self):
+    return self.limit - self.idx
+
+  def buffer(self):
+    return self.buf
+
+  def pos(self):
+    return self.idx
+
+  def skip(self, n):
+    if self.idx + n > self.limit: raise ProtocolBufferDecodeError("truncated")
+    self.idx += n
+    return
+
+  def skipData(self, tag):
+    t = tag & 7
+    if t == Encoder.NUMERIC:
+      self.getVarInt64()
+    elif t == Encoder.DOUBLE:
+      self.skip(8)
+    elif t == Encoder.STRING:
+      n = self.getVarInt32()
+      self.skip(n)
+    elif t == Encoder.STARTGROUP:
+      while 1:
+        t = self.getVarInt32()
+        if (t & 7) == Encoder.ENDGROUP:
+          break
+        else:
+          self.skipData(t)
+      if (t - Encoder.ENDGROUP) != (tag - Encoder.STARTGROUP):
+        raise ProtocolBufferDecodeError("corrupted")
+    elif t == Encoder.ENDGROUP:
+      raise ProtocolBufferDecodeError("corrupted")
+    elif t == Encoder.FLOAT:
+      self.skip(4)
+    else:
+      raise ProtocolBufferDecodeError("corrupted")
+
+
+  def get8(self):
+    if self.idx >= self.limit: raise ProtocolBufferDecodeError("truncated")
+    c = self.buf[self.idx]
+    self.idx += 1
+    return c
+
+  def get16(self):
+    if self.idx + 2 > self.limit: raise ProtocolBufferDecodeError("truncated")
+    c = self.buf[self.idx]
+    d = self.buf[self.idx + 1]
+    self.idx += 2
+    return (d << 8) | c
+
+  def get32(self):
+    if self.idx + 4 > self.limit: raise ProtocolBufferDecodeError("truncated")
+    c = self.buf[self.idx]
+    d = self.buf[self.idx + 1]
+    e = self.buf[self.idx + 2]
+    f = self.buf[self.idx + 3]
+    self.idx += 4
+    return (f << 24) | (e << 16) | (d << 8) | c
+
+  def get64(self):
+    if self.idx + 8 > self.limit: raise ProtocolBufferDecodeError("truncated")
+    c = self.buf[self.idx]
+    d = self.buf[self.idx + 1]
+    e = self.buf[self.idx + 2]
+    f = self.buf[self.idx + 3]
+    g = self.buf[self.idx + 4]
+    h = self.buf[self.idx + 5]
+    i = self.buf[self.idx + 6]
+    j = self.buf[self.idx + 7]
+    self.idx += 8
+    return ((j << 56) | (i << 48) | (h << 40) | (g << 32) | (f << 24)
+            | (e << 16) | (d << 8) | c)
+
+  def getVarInt32(self):
+
+
+
+    b = self.get8()
+    if not (b & 128):
+      return b
+
+    result = 0
+    shift = 0
+
+    while 1:
+      result |= ((b & 127) << shift)
+      shift += 7
+      if not (b & 128):
+        if result >= 0x10000000000000000:
+          raise ProtocolBufferDecodeError("corrupted")
+        break
+      if shift >= 64: raise ProtocolBufferDecodeError("corrupted")
+      b = self.get8()
+
+    if result >= 0x8000000000000000:
+      result -= 0x10000000000000000
+    if result >= 0x80000000 or result < -0x80000000:
+      raise ProtocolBufferDecodeError("corrupted")
+    return result
+
+  def getVarInt64(self):
+    result = self.getVarUint64()
+    if result >= (1 << 63):
+      result -= (1 << 64)
+    return result
+
+  def getVarUint64(self):
+    result = 0
+    shift = 0
+    while 1:
+      if shift >= 64: raise ProtocolBufferDecodeError("corrupted")
+      b = self.get8()
+      result |= ((b & 127) << shift)
+      shift += 7
+      if not (b & 128):
+        if result >= (1 << 64): raise ProtocolBufferDecodeError("corrupted")
+        return result
+    return result
+
+  def getFloat(self):
+    if self.idx + 4 > self.limit: raise ProtocolBufferDecodeError("truncated")
+    a = self.buf[self.idx:self.idx+4]
+    self.idx += 4
+    return struct.unpack("<f", a)[0]
+
+  def getDouble(self):
+    if self.idx + 8 > self.limit: raise ProtocolBufferDecodeError("truncated")
+    a = self.buf[self.idx:self.idx+8]
+    self.idx += 8
+    return struct.unpack("<d", a)[0]
+
+  def getBoolean(self):
+    b = self.get8()
+    if b != 0 and b != 1: raise ProtocolBufferDecodeError("corrupted")
+    return b
+
+  def getPrefixedString(self):
+    length = self.getVarInt32()
+    if self.idx + length > self.limit:
+      raise ProtocolBufferDecodeError("truncated")
+    r = self.buf[self.idx : self.idx + length]
+    self.idx += length
+    return r.tobytes()
+
+  def getRawString(self):
+    r = self.buf[self.idx:self.limit]
+    self.idx = self.limit
+    return r.tobytes()
+
+  _TYPE_TO_METHOD = {
+      TYPE_DOUBLE:   getDouble,
+      TYPE_FLOAT:    getFloat,
+      TYPE_FIXED64:  get64,
+      TYPE_FIXED32:  get32,
+      TYPE_INT32:    getVarInt32,
+      TYPE_INT64:    getVarInt64,
+      TYPE_UINT64:   getVarUint64,
+      TYPE_BOOL:     getBoolean,
+      TYPE_STRING:   getPrefixedString }
+
+
+
+
+
+class ExtensionIdentifier(object):
+  __slots__ = ('full_name', 'number', 'field_type', 'wire_tag', 'is_repeated',
+               'default', 'containing_cls', 'composite_cls', 'message_name')
+  def __init__(self, full_name, number, field_type, wire_tag, is_repeated,
+               default):
+    self.full_name = full_name
+    self.number = number
+    self.field_type = field_type
+    self.wire_tag = wire_tag
+    self.is_repeated = is_repeated
+    self.default = default
+
+class ExtendableProtocolMessage(ProtocolMessage):
+  def HasExtension(self, extension):
+
+    self._VerifyExtensionIdentifier(extension)
+    return extension in self._extension_fields
+
+  def ClearExtension(self, extension):
+
+
+    self._VerifyExtensionIdentifier(extension)
+    if extension in self._extension_fields:
+      del self._extension_fields[extension]
+
+  def GetExtension(self, extension, index=None):
+
+
+
+
+
+
+
+
+
+
+
+    self._VerifyExtensionIdentifier(extension)
+    if extension in self._extension_fields:
+      result = self._extension_fields[extension]
+    else:
+      if extension.is_repeated:
+        result = []
+      elif extension.composite_cls:
+        result = extension.composite_cls()
+      else:
+        result = extension.default
+    if extension.is_repeated:
+      result = result[index]
+    return result
+
+  def SetExtension(self, extension, *args):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    self._VerifyExtensionIdentifier(extension)
+    if extension.composite_cls:
+      raise TypeError(
+          'Cannot assign to extension "%s" because it is a composite type.' %
+          extension.full_name)
+    if extension.is_repeated:
+      try:
+        index, value = args
+      except ValueError:
+        raise TypeError(
+            "SetExtension(extension, index, value) for repeated extension "
+            "takes exactly 4 arguments: (%d given)" % (len(args) + 2))
+      self._extension_fields[extension][index] = value
+    else:
+      try:
+        (value,) = args
+      except ValueError:
+        raise TypeError(
+            "SetExtension(extension, value) for singular extension "
+            "takes exactly 3 arguments: (%d given)" % (len(args) + 2))
+      self._extension_fields[extension] = value
+
+  def MutableExtension(self, extension, index=None):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    self._VerifyExtensionIdentifier(extension)
+    if extension.composite_cls is None:
+      raise TypeError(
+          'MutableExtension() cannot be applied to "%s", because it is not a '
+          'composite type.' % extension.full_name)
+    if extension.is_repeated:
+      if index is None:
+        raise TypeError(
+            'MutableExtension(extension, index) for repeated extension '
+            'takes exactly 2 arguments: (1 given)')
+      return self.GetExtension(extension, index)
+    if extension in self._extension_fields:
+      return self._extension_fields[extension]
+    else:
+      result = extension.composite_cls()
+      self._extension_fields[extension] = result
+      return result
+
+  def ExtensionList(self, extension):
+
+
+
+
+
+    self._VerifyExtensionIdentifier(extension)
+    if not extension.is_repeated:
+      raise TypeError(
+          'ExtensionList() cannot be applied to "%s", because it is not a '
+          'repeated extension.' % extension.full_name)
+    if extension in self._extension_fields:
+      return self._extension_fields[extension]
+    result = []
+    self._extension_fields[extension] = result
+    return result
+
+  def ExtensionSize(self, extension):
+
+
+
+
+
+    self._VerifyExtensionIdentifier(extension)
+    if not extension.is_repeated:
+      raise TypeError(
+          'ExtensionSize() cannot be applied to "%s", because it is not a '
+          'repeated extension.' % extension.full_name)
+    if extension in self._extension_fields:
+      return len(self._extension_fields[extension])
+    return 0
+
+  def AddExtension(self, extension, value=None):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    self._VerifyExtensionIdentifier(extension)
+    if not extension.is_repeated:
+      raise TypeError(
+          'AddExtension() cannot be applied to "%s", because it is not a '
+          'repeated extension.' % extension.full_name)
+    if extension in self._extension_fields:
+      field = self._extension_fields[extension]
+    else:
+      field = []
+      self._extension_fields[extension] = field
+
+    if extension.composite_cls:
+      if value is not None:
+        raise TypeError(
+            'value must not be set in AddExtension() for "%s", because it is '
+            'a message type extension. Set values on the returned message '
+            'instead.' % extension.full_name)
+      msg = extension.composite_cls()
+      field.append(msg)
+      return msg
+
+    field.append(value)
+
+  def _VerifyExtensionIdentifier(self, extension):
+    if extension.containing_cls != self.__class__:
+      raise TypeError("Containing type of %s is %s, but not %s."
+                      % (extension.full_name,
+                         extension.containing_cls.__name__,
+                         self.__class__.__name__))
+
+  def _MergeExtensionFields(self, x):
+    for ext, val in x._extension_fields.items():
+      if ext.is_repeated:
+        for single_val in val:
+          if ext.composite_cls is None:
+            self.AddExtension(ext, single_val)
+          else:
+            self.AddExtension(ext).MergeFrom(single_val)
+      else:
+        if ext.composite_cls is None:
+          self.SetExtension(ext, val)
+        else:
+          self.MutableExtension(ext).MergeFrom(val)
+
+  def _ListExtensions(self):
+    return sorted(
+        (ext for ext in self._extension_fields
+         if (not ext.is_repeated) or self.ExtensionSize(ext) > 0),
+        key=lambda item: item.number)
+
+  def _ExtensionEquals(self, x):
+    extensions = self._ListExtensions()
+    if extensions != x._ListExtensions():
+      return False
+    for ext in extensions:
+      if ext.is_repeated:
+        if self.ExtensionSize(ext) != x.ExtensionSize(ext): return False
+        for e1, e2 in itertools.izip(self.ExtensionList(ext),
+                                     x.ExtensionList(ext)):
+          if e1 != e2: return False
+      else:
+        if self.GetExtension(ext) != x.GetExtension(ext): return False
+    return True
+
+  def _OutputExtensionFields(self, out, partial, extensions, start_index,
+                             end_field_number):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    def OutputSingleField(ext, value):
+      out.putVarInt32(ext.wire_tag)
+      if ext.field_type == TYPE_GROUP:
+        if partial:
+          value.OutputPartial(out)
+        else:
+          value.OutputUnchecked(out)
+        out.putVarInt32(ext.wire_tag + 1)
+      elif ext.field_type == TYPE_FOREIGN:
+        if partial:
+          out.putVarInt32(value.ByteSizePartial())
+          value.OutputPartial(out)
+        else:
+          out.putVarInt32(value.ByteSize())
+          value.OutputUnchecked(out)
+      else:
+        Encoder._TYPE_TO_METHOD[ext.field_type](out, value)
+
+    for ext_index, ext in enumerate(
+        itertools.islice(extensions, start_index, None), start=start_index):
+      if ext.number >= end_field_number:
+
+        return ext_index
+      if ext.is_repeated:
+        for field in self._extension_fields[ext]:
+          OutputSingleField(ext, field)
+      else:
+        OutputSingleField(ext, self._extension_fields[ext])
+    return len(extensions)
+
+  def _ParseOneExtensionField(self, wire_tag, d):
+    number = wire_tag >> 3
+    if number in self._extensions_by_field_number:
+      ext = self._extensions_by_field_number[number]
+      if wire_tag != ext.wire_tag:
+
+        return
+      if ext.field_type == TYPE_FOREIGN:
+        length = d.getVarInt32()
+        tmp = Decoder(d.buffer(), d.pos(), d.pos() + length)
+        if ext.is_repeated:
+          self.AddExtension(ext).TryMerge(tmp)
+        else:
+          self.MutableExtension(ext).TryMerge(tmp)
+        d.skip(length)
+      elif ext.field_type == TYPE_GROUP:
+        if ext.is_repeated:
+          self.AddExtension(ext).TryMerge(d)
+        else:
+          self.MutableExtension(ext).TryMerge(d)
+      else:
+        value = Decoder._TYPE_TO_METHOD[ext.field_type](d)
+        if ext.is_repeated:
+          self.AddExtension(ext, value)
+        else:
+          self.SetExtension(ext, value)
+    else:
+
+      d.skipData(wire_tag)
+
+  def _ExtensionByteSize(self, partial):
+    size = 0
+    for extension, value in self._extension_fields.iteritems():
+      ftype = extension.field_type
+      tag_size = self.lengthVarInt64(extension.wire_tag)
+      if ftype == TYPE_GROUP:
+        tag_size *= 2
+      if extension.is_repeated:
+        size += tag_size * len(value)
+        for single_value in value:
+          size += self._FieldByteSize(ftype, single_value, partial)
+      else:
+        size += tag_size + self._FieldByteSize(ftype, value, partial)
+    return size
+
+  def _FieldByteSize(self, ftype, value, partial):
+    size = 0
+    if ftype == TYPE_STRING:
+      size = self.lengthString(len(value))
+    elif ftype == TYPE_FOREIGN or ftype == TYPE_GROUP:
+      if partial:
+        size = self.lengthString(value.ByteSizePartial())
+      else:
+        size = self.lengthString(value.ByteSize())
+    elif ftype == TYPE_INT64 or ftype == TYPE_UINT64 or ftype == TYPE_INT32:
+      size = self.lengthVarInt64(value)
+    else:
+      if ftype in Encoder._TYPE_TO_BYTE_SIZE:
+        size = Encoder._TYPE_TO_BYTE_SIZE[ftype]
+      else:
+        raise AssertionError(
+            'Extension type %d is not recognized.' % ftype)
+    return size
+
+  def _ExtensionDebugString(self, prefix, printElemNumber):
+    res = ''
+    extensions = self._ListExtensions()
+    for extension in extensions:
+      value = self._extension_fields[extension]
+      if extension.is_repeated:
+        cnt = 0
+        for e in value:
+          elm=""
+          if printElemNumber: elm = "(%d)" % cnt
+          if extension.composite_cls is not None:
+            res += prefix + "[%s%s] {\n" % (extension.full_name, elm)
+            res += e.__str__(prefix + "  ", printElemNumber)
+            res += prefix + "}\n"
+      else:
+        if extension.composite_cls is not None:
+          res += prefix + "[%s] {\n" % extension.full_name
+          res += value.__str__(
+              prefix + "  ", printElemNumber)
+          res += prefix + "}\n"
+        else:
+          if extension.field_type in _TYPE_TO_DEBUG_STRING:
+            text_value = _TYPE_TO_DEBUG_STRING[
+                extension.field_type](self, value)
+          else:
+            text_value = self.DebugFormat(value)
+          res += prefix + "[%s]: %s\n" % (extension.full_name, text_value)
+    return res
+
+  @staticmethod
+  def _RegisterExtension(cls, extension, composite_cls=None):
+    extension.containing_cls = cls
+    extension.composite_cls = composite_cls
+    if composite_cls is not None:
+      extension.message_name = composite_cls._PROTO_DESCRIPTOR_NAME
+    actual_handle = cls._extensions_by_field_number.setdefault(
+        extension.number, extension)
+    if actual_handle is not extension:
+      raise AssertionError(
+          'Extensions "%s" and "%s" both try to extend message type "%s" with '
+          'field number %d.' %
+          (extension.full_name, actual_handle.full_name,
+           cls.__name__, extension.number))
diff --git a/third_party/google/net/proto/__init__.py b/third_party/google/net/proto/__init__.py
new file mode 100644
index 0000000..93e6786
--- /dev/null
+++ b/third_party/google/net/proto/__init__.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google LLC
+#
+# 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.
+#