/*
 * Copyright (C) 2013 Colin Walters <walters@verbum.org>
 *
 * SPDX-License-Identifier: LGPL-2.0+
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <https://www.gnu.org/licenses/>.
 */

/* Significant code derived from protobuf: */

/*
 * Protocol Buffers - Google's data interchange format
 * Copyright 2008 Google Inc.  All rights reserved.
 * http: *code.google.com/p/protobuf/
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. 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.
*/

#include "config.h"

#include "ostree-varint.h"

static const int max_varint_bytes = 10;

/**
 * _ostree_read_varuint64:
 * @buf: (array length=buflen): Byte buffer
 * @buflen: Length of bytes in @buf
 * @out_value: (out): Value
 * @bytes_read: (out): Number of bytes read
 *
 * Returns: %TRUE on success, %FALSE on end of stream
 */
gboolean
_ostree_read_varuint64 (const guint8   *buf,
                        gsize           buflen,
                        guint64        *out_value,
                        gsize          *bytes_read)
{
  guint64 result = 0;
  int count = 0;
  guint8 b;
  
  /* Adapted from CodedInputStream::ReadVarint64Slow */

  do
    {
      if (count == max_varint_bytes)
        return FALSE;
      if (buflen == 0)
        return FALSE;

      b = *buf;
      result |= ((guint64)(b & 0x7F)) << (7 * count);
      buf++;
      buflen--;
      ++count;
  } while (b & 0x80);

  *bytes_read = count;
  *out_value = result;

  return TRUE;
}

/**
 * _ostree_write_varuint64:
 * @buf: Target buffer (will contain binary data)
 * @n: Integer to encode
 *
 * Append a varint-encoded version of @n to @buf.
 */
void
_ostree_write_varuint64 (GString *buf, guint64 n)
{
  /* Splitting into 32-bit pieces gives better performance on 32-bit
   * processors. */
  guint32 part0 = (guint32)n;
  guint32 part1 = (guint32)(n >> 28);
  guint32 part2 = (guint32)(n >> 56);
  guint8 target[10];
  int i;
  int size;

  /*
   * Here we can't really optimize for small numbers, since the value is
   * split into three parts.  Cheking for numbers < 128, for instance,
   * would require three comparisons, since you'd have to make sure part1
   * and part2 are zero.  However, if the caller is using 64-bit integers,
   * it is likely that they expect the numbers to often be very large, so
   * we probably don't want to optimize for small numbers anyway.  Thus,
   * we end up with a hardcoded binary search tree...
   */
  if (part2 == 0) {
    if (part1 == 0) {
      if (part0 < (1 << 14)) {
        if (part0 < (1 << 7)) {
          size = 1; goto size1;
        } else {
          size = 2; goto size2;
        }
      } else {
        if (part0 < (1 << 21)) {
          size = 3; goto size3;
        } else {
          size = 4; goto size4;
        }
      }
    } else {
      if (part1 < (1 << 14)) {
        if (part1 < (1 << 7)) {
          size = 5; goto size5;
        } else {
          size = 6; goto size6;
        }
      } else {
        if (part1 < (1 << 21)) {
          size = 7; goto size7;
        } else {
          size = 8; goto size8;
        }
      }
    }
  } else {
    if (part2 < (1 << 7)) {
      size = 9; goto size9;
    } else {
      size = 10; goto size10;
    }
  }

  g_assert_not_reached ();

  size10: target[9] = (guint8)((part2 >>  7) | 0x80);
  size9 : target[8] = (guint8)((part2      ) | 0x80);
  size8 : target[7] = (guint8)((part1 >> 21) | 0x80);
  size7 : target[6] = (guint8)((part1 >> 14) | 0x80);
  size6 : target[5] = (guint8)((part1 >>  7) | 0x80);
  size5 : target[4] = (guint8)((part1      ) | 0x80);
  size4 : target[3] = (guint8)((part0 >> 21) | 0x80);
  size3 : target[2] = (guint8)((part0 >> 14) | 0x80);
  size2 : target[1] = (guint8)((part0 >>  7) | 0x80);
  size1 : target[0] = (guint8)((part0      ) | 0x80);

  target[size-1] &= 0x7F;

  for (i = 0; i < size; i++)
    g_string_append_c (buf, target[i]);
}
