<commit_before>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 <google/protobuf/util/json_util.h>

#include <list>
#include <string>

#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/util/json_format_proto3.pb.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/util/type_resolver_util.h>
#include <gtest/gtest.h>

namespace google {
namespace protobuf {
namespace util {
namespace {

using proto3::FOO;
using proto3::BAR;
using proto3::TestMessage;
using proto3::TestMap;

static const char kTypeUrlPrefix[] = "type.googleapis.com";

static string GetTypeUrl(const Descriptor* message) {
  return string(kTypeUrlPrefix) + "/" + message->full_name();
}

// As functions defined in json_util.h are just thin wrappers around the
// JSON conversion code in //net/proto2/util/converter, in this test we
// only cover some very basic cases to make sure the wrappers have forwarded
// parameters to the underlying implementation correctly. More detailed
// tests are contained in the //net/proto2/util/converter directory.
class JsonUtilTest : public testing::Test {
 protected:
  JsonUtilTest() {
    resolver_.reset(NewTypeResolverForDescriptorPool(
        kTypeUrlPrefix, DescriptorPool::generated_pool()));
  }

  string ToJson(const Message& message, const JsonOptions& options) {
    string result;
    GOOGLE_CHECK_OK(BinaryToJsonString(resolver_.get(),
                                GetTypeUrl(message.GetDescriptor()),
                                message.SerializeAsString(), &result, options));
    return result;
  }

  bool FromJson(const string& json, Message* message) {
    string binary;
    GOOGLE_CHECK_OK(JsonToBinaryString(
        resolver_.get(), GetTypeUrl(message->GetDescriptor()), json, &binary));
    return message->ParseFromString(binary);
  }

  google::protobuf::scoped_ptr<TypeResolver> resolver_;
};

TEST_F(JsonUtilTest, TestWhitespaces) {
  TestMessage m;
  m.mutable_message_value();

  JsonOptions options;
  EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options));
  options.add_whitespace = true;
  EXPECT_EQ(
      "{\n"
      " \"messageValue\": {}\n"
      "}\n",
      ToJson(m, options));
}

// TODO(skarvaje): Uncomment after cl/96232915 is submitted.
// TEST_F(JsonUtilTest, TestDefaultValues) {
  // TestMessage m;
  // JsonOptions options;
  // EXPECT_EQ("{}", ToJson(m, options));
  // options.always_print_primitive_fields = true;
  // EXPECT_EQ(
      // "{\"boolValue\":false,"
      // "\"int32Value\":0,"
      // "\"int64Value\":\"0\","
      // "\"uint32Value\":0,"
      // "\"uint64Value\":\"0\","
      // "\"floatValue\":0,"
      // "\"doubleValue\":0,"
      // "\"stringValue\":\"\","
      // "\"bytesValue\":\"\","
      // // TODO(xiaofeng): The default enum value should be FOO. I believe
      // // this is a bug in DefaultValueObjectWriter.
      // "\"enumValue\":null"
      // "}",
      // ToJson(m, options));
// }

TEST_F(JsonUtilTest, ParseMessage) {
  // Some random message but good enough to verify that the parsing warpper
  // functions are working properly.
  string input =
      "{\n"
      "  \"int32Value\": 1024,\n"
      "  \"repeatedInt32Value\": [1, 2],\n"
      "  \"messageValue\": {\n"
      "    \"value\": 2048\n"
      "  },\n"
      "  \"repeatedMessageValue\": [\n"
      "    {\"value\": 40}, {\"value\": 96}\n"
      "  ]\n"
      "}\n";
  TestMessage m;
  ASSERT_TRUE(FromJson(input, &m));
  EXPECT_EQ(1024, m.int32_value());
  ASSERT_EQ(2, m.repeated_int32_value_size());
  EXPECT_EQ(1, m.repeated_int32_value(0));
  EXPECT_EQ(2, m.repeated_int32_value(1));
  EXPECT_EQ(2048, m.message_value().value());
  ASSERT_EQ(2, m.repeated_message_value_size());
  EXPECT_EQ(40, m.repeated_message_value(0).value());
  EXPECT_EQ(96, m.repeated_message_value(1).value());
}

TEST_F(JsonUtilTest, ParseMap) {
  TestMap message;
  (*message.mutable_string_map())["hello"] = 1234;
  JsonOptions options;
  EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, options));
  TestMap other;
  ASSERT_TRUE(FromJson(ToJson(message, options), &other));
  EXPECT_EQ(message.DebugString(), other.DebugString());
}

typedef pair<char*, int> Segment;
// A ZeroCopyOutputStream that writes to multiple buffers.
class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream {
 public:
  explicit SegmentedZeroCopyOutputStream(list<Segment> segments)
      : segments_(segments), last_segment_((char*)NULL, 0), byte_count_(0) {}

  virtual bool Next(void** buffer, int* length) {
    if (segments_.empty()) {
      return false;
    }
    last_segment_ = segments_.front();
    segments_.pop_front();
    *buffer = last_segment_.first;
    *length = last_segment_.second;
    byte_count_ += *length;
    return true;
  }

  virtual void BackUp(int length) {
    GOOGLE_CHECK(length <= last_segment_.second);
    segments_.push_front(
        Segment(last_segment_.first + last_segment_.second - length, length));
    last_segment_ = Segment(last_segment_.first, last_segment_.second - length);
    byte_count_ -= length;
  }

  virtual int64 ByteCount() const { return byte_count_; }

 private:
  list<Segment> segments_;
  Segment last_segment_;
  int64 byte_count_;
};

// This test splits the output buffer and also the input data into multiple
// segments and checks that the implementation of ZeroCopyStreamByteSink
// handles all possible cases correctly.
TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) {
  static const int kOutputBufferLength = 10;
  // An exhaustive test takes too long, skip some combinations to make the test
  // run faster.
  static const int kSkippedPatternCount = 7;

  char buffer[kOutputBufferLength];
  for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1));
       split_pattern += kSkippedPatternCount) {
    // Split the buffer into small segments according to the split_pattern.
    list<Segment> segments;
    int segment_start = 0;
    for (int i = 0; i < kOutputBufferLength - 1; ++i) {
      if (split_pattern & (1 << i)) {
        segments.push_back(
            Segment(buffer + segment_start, i - segment_start + 1));
        segment_start = i + 1;
      }
    }
    segments.push_back(
        Segment(buffer + segment_start, kOutputBufferLength - segment_start));

    // Write exactly 10 bytes through the ByteSink.
    string input_data = "0123456789";
    for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
         input_pattern += kSkippedPatternCount) {
      memset(buffer, 0, sizeof(buffer));
      {
        SegmentedZeroCopyOutputStream output_stream(segments);
        internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
        int start = 0;
        for (int j = 0; j < input_data.length() - 1; ++j) {
          if (input_pattern & (1 << j)) {
            byte_sink.Append(&input_data[start], j - start + 1);
            start = j + 1;
          }
        }
        byte_sink.Append(&input_data[start], input_data.length() - start);
      }
      EXPECT_EQ(input_data, string(buffer, input_data.length()));
    }

    // Write only 9 bytes through the ByteSink.
    input_data = "012345678";
    for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
         input_pattern += kSkippedPatternCount) {
      memset(buffer, 0, sizeof(buffer));
      {
        SegmentedZeroCopyOutputStream output_stream(segments);
        internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
        int start = 0;
        for (int j = 0; j < input_data.length() - 1; ++j) {
          if (input_pattern & (1 << j)) {
            byte_sink.Append(&input_data[start], j - start + 1);
            start = j + 1;
          }
        }
        byte_sink.Append(&input_data[start], input_data.length() - start);
      }
      EXPECT_EQ(input_data, string(buffer, input_data.length()));
      EXPECT_EQ(0, buffer[input_data.length()]);
    }

    // Write 11 bytes through the ByteSink. The extra byte will just
    // be ignored.
    input_data = "0123456789A";
    for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
         input_pattern += kSkippedPatternCount) {
      memset(buffer, 0, sizeof(buffer));
      {
        SegmentedZeroCopyOutputStream output_stream(segments);
        internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
        int start = 0;
        for (int j = 0; j < input_data.length() - 1; ++j) {
          if (input_pattern & (1 << j)) {
            byte_sink.Append(&input_data[start], j - start + 1);
            start = j + 1;
          }
        }
        byte_sink.Append(&input_data[start], input_data.length() - start);
      }
      EXPECT_EQ(input_data.substr(0, kOutputBufferLength),
                string(buffer, kOutputBufferLength));
    }
  }
}

}  // namespace
}  // namespace util
}  // namespace protobuf
}  // namespace google
<commit_msg>Use static cast<commit_after>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 <google/protobuf/util/json_util.h>

#include <list>
#include <string>

#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/util/json_format_proto3.pb.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/util/type_resolver_util.h>
#include <gtest/gtest.h>

namespace google {
namespace protobuf {
namespace util {
namespace {

using proto3::FOO;
using proto3::BAR;
using proto3::TestMessage;
using proto3::TestMap;

static const char kTypeUrlPrefix[] = "type.googleapis.com";

static string GetTypeUrl(const Descriptor* message) {
  return string(kTypeUrlPrefix) + "/" + message->full_name();
}

// As functions defined in json_util.h are just thin wrappers around the
// JSON conversion code in //net/proto2/util/converter, in this test we
// only cover some very basic cases to make sure the wrappers have forwarded
// parameters to the underlying implementation correctly. More detailed
// tests are contained in the //net/proto2/util/converter directory.
class JsonUtilTest : public testing::Test {
 protected:
  JsonUtilTest() {
    resolver_.reset(NewTypeResolverForDescriptorPool(
        kTypeUrlPrefix, DescriptorPool::generated_pool()));
  }

  string ToJson(const Message& message, const JsonOptions& options) {
    string result;
    GOOGLE_CHECK_OK(BinaryToJsonString(resolver_.get(),
                                GetTypeUrl(message.GetDescriptor()),
                                message.SerializeAsString(), &result, options));
    return result;
  }

  bool FromJson(const string& json, Message* message) {
    string binary;
    GOOGLE_CHECK_OK(JsonToBinaryString(
        resolver_.get(), GetTypeUrl(message->GetDescriptor()), json, &binary));
    return message->ParseFromString(binary);
  }

  google::protobuf::scoped_ptr<TypeResolver> resolver_;
};

TEST_F(JsonUtilTest, TestWhitespaces) {
  TestMessage m;
  m.mutable_message_value();

  JsonOptions options;
  EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options));
  options.add_whitespace = true;
  EXPECT_EQ(
      "{\n"
      " \"messageValue\": {}\n"
      "}\n",
      ToJson(m, options));
}

// TODO(skarvaje): Uncomment after cl/96232915 is submitted.
// TEST_F(JsonUtilTest, TestDefaultValues) {
  // TestMessage m;
  // JsonOptions options;
  // EXPECT_EQ("{}", ToJson(m, options));
  // options.always_print_primitive_fields = true;
  // EXPECT_EQ(
      // "{\"boolValue\":false,"
      // "\"int32Value\":0,"
      // "\"int64Value\":\"0\","
      // "\"uint32Value\":0,"
      // "\"uint64Value\":\"0\","
      // "\"floatValue\":0,"
      // "\"doubleValue\":0,"
      // "\"stringValue\":\"\","
      // "\"bytesValue\":\"\","
      // // TODO(xiaofeng): The default enum value should be FOO. I believe
      // // this is a bug in DefaultValueObjectWriter.
      // "\"enumValue\":null"
      // "}",
      // ToJson(m, options));
// }

TEST_F(JsonUtilTest, ParseMessage) {
  // Some random message but good enough to verify that the parsing warpper
  // functions are working properly.
  string input =
      "{\n"
      "  \"int32Value\": 1024,\n"
      "  \"repeatedInt32Value\": [1, 2],\n"
      "  \"messageValue\": {\n"
      "    \"value\": 2048\n"
      "  },\n"
      "  \"repeatedMessageValue\": [\n"
      "    {\"value\": 40}, {\"value\": 96}\n"
      "  ]\n"
      "}\n";
  TestMessage m;
  ASSERT_TRUE(FromJson(input, &m));
  EXPECT_EQ(1024, m.int32_value());
  ASSERT_EQ(2, m.repeated_int32_value_size());
  EXPECT_EQ(1, m.repeated_int32_value(0));
  EXPECT_EQ(2, m.repeated_int32_value(1));
  EXPECT_EQ(2048, m.message_value().value());
  ASSERT_EQ(2, m.repeated_message_value_size());
  EXPECT_EQ(40, m.repeated_message_value(0).value());
  EXPECT_EQ(96, m.repeated_message_value(1).value());
}

TEST_F(JsonUtilTest, ParseMap) {
  TestMap message;
  (*message.mutable_string_map())["hello"] = 1234;
  JsonOptions options;
  EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, options));
  TestMap other;
  ASSERT_TRUE(FromJson(ToJson(message, options), &other));
  EXPECT_EQ(message.DebugString(), other.DebugString());
}

typedef pair<char*, int> Segment;
// A ZeroCopyOutputStream that writes to multiple buffers.
class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream {
 public:
  explicit SegmentedZeroCopyOutputStream(list<Segment> segments)
      : segments_(segments), last_segment_(static_cast<char*>(NULL), 0), byte_count_(0) {}

  virtual bool Next(void** buffer, int* length) {
    if (segments_.empty()) {
      return false;
    }
    last_segment_ = segments_.front();
    segments_.pop_front();
    *buffer = last_segment_.first;
    *length = last_segment_.second;
    byte_count_ += *length;
    return true;
  }

  virtual void BackUp(int length) {
    GOOGLE_CHECK(length <= last_segment_.second);
    segments_.push_front(
        Segment(last_segment_.first + last_segment_.second - length, length));
    last_segment_ = Segment(last_segment_.first, last_segment_.second - length);
    byte_count_ -= length;
  }

  virtual int64 ByteCount() const { return byte_count_; }

 private:
  list<Segment> segments_;
  Segment last_segment_;
  int64 byte_count_;
};

// This test splits the output buffer and also the input data into multiple
// segments and checks that the implementation of ZeroCopyStreamByteSink
// handles all possible cases correctly.
TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) {
  static const int kOutputBufferLength = 10;
  // An exhaustive test takes too long, skip some combinations to make the test
  // run faster.
  static const int kSkippedPatternCount = 7;

  char buffer[kOutputBufferLength];
  for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1));
       split_pattern += kSkippedPatternCount) {
    // Split the buffer into small segments according to the split_pattern.
    list<Segment> segments;
    int segment_start = 0;
    for (int i = 0; i < kOutputBufferLength - 1; ++i) {
      if (split_pattern & (1 << i)) {
        segments.push_back(
            Segment(buffer + segment_start, i - segment_start + 1));
        segment_start = i + 1;
      }
    }
    segments.push_back(
        Segment(buffer + segment_start, kOutputBufferLength - segment_start));

    // Write exactly 10 bytes through the ByteSink.
    string input_data = "0123456789";
    for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
         input_pattern += kSkippedPatternCount) {
      memset(buffer, 0, sizeof(buffer));
      {
        SegmentedZeroCopyOutputStream output_stream(segments);
        internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
        int start = 0;
        for (int j = 0; j < input_data.length() - 1; ++j) {
          if (input_pattern & (1 << j)) {
            byte_sink.Append(&input_data[start], j - start + 1);
            start = j + 1;
          }
        }
        byte_sink.Append(&input_data[start], input_data.length() - start);
      }
      EXPECT_EQ(input_data, string(buffer, input_data.length()));
    }

    // Write only 9 bytes through the ByteSink.
    input_data = "012345678";
    for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
         input_pattern += kSkippedPatternCount) {
      memset(buffer, 0, sizeof(buffer));
      {
        SegmentedZeroCopyOutputStream output_stream(segments);
        internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
        int start = 0;
        for (int j = 0; j < input_data.length() - 1; ++j) {
          if (input_pattern & (1 << j)) {
            byte_sink.Append(&input_data[start], j - start + 1);
            start = j + 1;
          }
        }
        byte_sink.Append(&input_data[start], input_data.length() - start);
      }
      EXPECT_EQ(input_data, string(buffer, input_data.length()));
      EXPECT_EQ(0, buffer[input_data.length()]);
    }

    // Write 11 bytes through the ByteSink. The extra byte will just
    // be ignored.
    input_data = "0123456789A";
    for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
         input_pattern += kSkippedPatternCount) {
      memset(buffer, 0, sizeof(buffer));
      {
        SegmentedZeroCopyOutputStream output_stream(segments);
        internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
        int start = 0;
        for (int j = 0; j < input_data.length() - 1; ++j) {
          if (input_pattern & (1 << j)) {
            byte_sink.Append(&input_data[start], j - start + 1);
            start = j + 1;
          }
        }
        byte_sink.Append(&input_data[start], input_data.length() - start);
      }
      EXPECT_EQ(input_data.substr(0, kOutputBufferLength),
                string(buffer, kOutputBufferLength));
    }
  }
}

}  // namespace
}  // namespace util
}  // namespace protobuf
}  // namespace google
<|endoftext|>


<commit_before>//
// (c) Copyright 2017 DESY,ESS
//
// This file is part of h5cpp.
//
// 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.1 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 ofMERCHANTABILITY
// 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, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
// Boston, MA  02110-1301 USA
// ===========================================================================
//
// Author: Eugen Wintersberger <eugen.wintersberger@desy.de>
// Created on: Nov 17, 2017
//
#pragma once

#include <functional>
#include <h5cpp/node/group.hpp>
#include <h5cpp/node/node_iterator.hpp>
#include <h5cpp/core/windows.hpp>

namespace hdf5 {
namespace node {

//!
//! \brief recursive node iterator
//!
//! Iterator which recursively iterates over all subelements of a particular
//! group.
//! All relevant constructors are private. To construct an instance of
//! this iterator use the two static factory functions begin() and end().
//!
//! \code
//! auto iter = RecursiveNodeIterator::begin();
//! auto end  = RecursiveNodeIterator::end();
//!
//! while(iter != end)
//! {
//!   std::cout<<
//! }
//!
//!
class DLL_EXPORT RecursiveNodeIterator
{
  public:
    using value_type = Node;
    using pointer = value_type*;
    using reference = value_type&;
    using difference_type = ssize_t;
    using iterator_category = std::random_access_iterator_tag;

    //!
    //! \brief default constructor
    //!
    //! There is no default constructors -> would not make too much sense.
    //!
    RecursiveNodeIterator() = delete;

    //!
    //! \brief copy constructor
    //!
    //! Required, but we can use the compiler provided default version here.
    //!
    RecursiveNodeIterator(const RecursiveNodeIterator&) = default;

    static RecursiveNodeIterator begin(const Group &current_group);
    static RecursiveNodeIterator end(const Group &current_group);

    Node operator*() const;

    Node *operator->();

    RecursiveNodeIterator &operator++();
    RecursiveNodeIterator operator++(int);

    bool operator==(const RecursiveNodeIterator &rhs) const;

    bool operator!=(const RecursiveNodeIterator &rhs) const;

  private:
    RecursiveNodeIterator(const Group &current_group,
                          RecursiveNodeIterator parent_iterator);
    RecursiveNodeIterator(const Group &current_group);

    using IteratorPointer = std::shared_ptr<RecursiveNodeIterator>;

    //!
    //! references the current group we are in. We have to keep this
    //! as an object we are iterating upon must not be destroyed.
    //!
    Group           current_group_;

    //!
    //! iterator over the current group.
    //!
    NodeIterator    current_iterator_;

    //!
    //! Pointer to the parent interator if there is one
    //!
    IteratorPointer parent_iterator_;

};

} // namespace node
} // namespace hdf5
<commit_msg>Updated documentation for RecursiveNodeIteratr<commit_after>//
// (c) Copyright 2017 DESY,ESS
//
// This file is part of h5cpp.
//
// 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.1 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 ofMERCHANTABILITY
// 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, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
// Boston, MA  02110-1301 USA
// ===========================================================================
//
// Author: Eugen Wintersberger <eugen.wintersberger@desy.de>
// Created on: Nov 17, 2017
//
#pragma once

#include <functional>
#include <h5cpp/node/group.hpp>
#include <h5cpp/node/node_iterator.hpp>
#include <h5cpp/core/windows.hpp>

namespace hdf5 {
namespace node {

//!
//! \brief recursive node iterator
//!
//! Iterator which recursively iterates over all subelements of a particular
//! group.
//! All relevant constructors are private. To construct an instance of
//! this iterator use the two static factory functions begin() and end().
//!
//! \code
//! Group top = ...;
//! auto iter = RecursiveNodeIterator::begin(top);
//! auto end  = RecursiveNodeIterator::end(top);
//!
//! while(iter != end)
//! {
//!   std::cout<<
//! }
//! // or with STL
//!
//! std::for_each(RecursiveNodeIterator::begin(top),
//!               RecursiveNodeIterator::end(top),
//!               [](const Node &node) { ...do something ... ; });
//! \endcode
//!
//! Unlike NodeIterator, RecursiveNodeIterator is a simple forward iterator.
//!
//!
class DLL_EXPORT RecursiveNodeIterator
{
  public:
    using value_type = Node;
    using pointer = value_type*;
    using reference = value_type&;
    using difference_type = ssize_t;
    using iterator_category = std::forward_iterator_tag;

    //!
    //! \brief default constructor
    //!
    //! There is no default constructors -> would not make too much sense.
    //!
    RecursiveNodeIterator() = delete;

    //!
    //! \brief copy constructor
    //!
    //! Required, but we can use the compiler provided default version here.
    //!
    RecursiveNodeIterator(const RecursiveNodeIterator&) = default;

    //!
    //! \brief create iterator to the first element
    //!
    //! Static factory function returning an instance of
    //! RecursiveForwardIterator on the first element of the top group.
    //!
    //! \param current_group the top level group where to start with
    //!                      recursive iteration
    //! \return instance of RecursiveNodeIterator
    //!
    static RecursiveNodeIterator begin(const Group &current_group);

    //!
    //! \brief create iterator to the last+1 element
    //!
    //! Static factory function returning an instance of
    //! RecursiveForwardIterator on the last+1 element of top group.
    //!
    //! \param current_group the top level group at which to start the
    //!                      recursive iteration.
    //! \return instance of RecursiveNodeIterator
    //!
    static RecursiveNodeIterator end(const Group &current_group);

    //!
    //! \brief return current Node instance
    //!
    //! Returns an instance of the actual node the iterator refers to.
    //! \return instance of node::Node
    //!
    Node operator*() const;

    //!
    //! \brief return address of the actual Node instance
    //!
    //! Returns the adress of the actual node in memory.
    //! \return adress of current Node
    //!
    Node *operator->();

    //!
    //! \brief advance to next element
    //!
    //! Advances to the next element in line.
    //! \code
    //! RecursiveNodeIterator iter = ...;
    //! while(...)
    //! {
    //!   ++iter;
    //! }
    //! \endcode
    //!
    //! \return reference to the udpated iterator
    //!
    RecursiveNodeIterator &operator++();

    //!
    //! \brief advance to next element
    //!
    //! Advances to the next element in line but returns an iterator to the
    //! previous element.
    //!
    //! \code
    //! RecursiveNodeIterator iter = ...;
    //!
    //! while(...)
    //! {
    //!   Node n = *iter++; //give me the actual node and advance
    //!   std::cout<<n.link().path()<<std::endl;
    //! }
    //! \endcode
    //!
    //! \return instance of RecursiveNodeIterator
    //!
    RecursiveNodeIterator operator++(int);

    bool operator==(const RecursiveNodeIterator &rhs) const;

    bool operator!=(const RecursiveNodeIterator &rhs) const;

  private:
    RecursiveNodeIterator(const Group &current_group,
                          RecursiveNodeIterator parent_iterator);
    RecursiveNodeIterator(const Group &current_group);

    using IteratorPointer = std::shared_ptr<RecursiveNodeIterator>;

    //!
    //! references the current group we are in. We have to keep this
    //! as an object we are iterating upon must not be destroyed.
    //!
    Group           current_group_;

    //!
    //! iterator over the current group.
    //!
    NodeIterator    current_iterator_;

    //!
    //! Pointer to the parent interator if there is one
    //!
    IteratorPointer parent_iterator_;

};

} // namespace node
} // namespace hdf5
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 German Mendez Bravo (Kronuz)
 *
 * 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.
 */

#ifndef MPH_HH
#define MPH_HH

#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>

namespace mph {

////////////////////////////////////////////////////////////////////////////////
// Constexpr linear congruential random number generator

template <typename T, T multiplier, T increment, T modulus>
class linear_congruential_generator {
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

	T seed = 1u;

public:
	constexpr T operator()() {
		seed = (seed * multiplier + increment) % modulus;
		return seed;
	}
};

////////////////////////////////////////////////////////////////////////////////
// Constexpr quicksort

template <class T>
constexpr void swap(T& a, T& b) {
	auto tmp = a;
	a = b;
	b = tmp;
}

template <typename It>
constexpr It partition(It left, It right) {
	auto pivot = left + (right - left) / 2;
	auto value = *pivot;
	swap(*right, *pivot);
	for (auto it = left; it < right; ++it) {
		if (*it < value) {
			swap(*it, *left);
			++left;
		}
	}
	swap(*right, *left);
	return left;
}

template <typename It>
constexpr void quicksort(It left, It right) {
	if (left < right) {
		auto pivot = partition(left, right);
		quicksort(left, pivot);
		quicksort(pivot + 1, right);
	}
}

////////////////////////////////////////////////////////////////////////////////
// Computes a constexpr minimal perfect hash table

constexpr static auto npos = std::numeric_limits<std::size_t>::max();

#define MPH_SORT_CLASHES

template <typename T, std::size_t N, typename RNG = linear_congruential_generator<T, 48271, 0, 2147483647>>
class mph {
	static_assert(N > 0, "Must have at least one element");
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

	struct hashed_item_t {
		T item;
		std::size_t slot;
		std::size_t cnt;

		constexpr hashed_item_t() : item{0}, slot{0}, cnt{0} { }

		constexpr hashed_item_t(const hashed_item_t& other) :
			item{other.item},
			slot{other.slot},
			cnt{other.cnt} { }

		constexpr hashed_item_t(hashed_item_t&& other) noexcept :
			item{std::move(other.item)},
			slot{std::move(other.slot)},
			cnt{std::move(other.cnt)} { }

		constexpr hashed_item_t& operator=(const hashed_item_t& other) {
			item = other.item;
			slot = other.slot;
			cnt = other.cnt;
			return *this;
		}

		constexpr hashed_item_t& operator=(hashed_item_t&& other) noexcept {
			item = std::move(other.item);
			slot = std::move(other.slot);
			cnt = std::move(other.cnt);
			return *this;
		}

		constexpr bool operator<(const hashed_item_t& other) const {
			if (cnt == other.cnt) {
				return slot < other.slot;
			}
			return cnt > other.cnt;
		}
	};

	T _index[N];
	T _items[N];

public:
	constexpr mph(const T (&items)[N]) : _index{}, _items{} {
		RNG rng;
		hashed_item_t hashed_items[N];

		for (std::size_t pos = 0; pos < N; ++pos) {
			auto& item = items[pos];
			auto& hashed_item = hashed_items[pos];
			auto hashed = item;
			hashed_item.item = item;
			hashed_item.slot = static_cast<std::size_t>(hashed % N);
		}

		quicksort(&hashed_items[0], &hashed_items[N - 1]);

		auto end = &hashed_items[N];
		auto frm = &hashed_items[0];
		auto to = frm;

#ifdef MPH_SORT_CLASHES
		do {
			++to;
			if (to == end || frm->slot != to->slot) {
				auto cnt = to - frm;
				for (; frm != to; ++frm) {
					frm->cnt = cnt;
				}
			}
		} while (to != end);

		quicksort(&hashed_items[0], &hashed_items[N - 1]);

		frm = &hashed_items[0];
		to = frm;
#endif

		do {
			++to;
			if (to == end || frm->slot != to->slot) {
				auto& index = _index[frm->slot];
				while (true) {
					auto rnd = rng();
					if (!rnd) {
						continue;
					}
					auto frm_ = frm;
					std::size_t used_zero = npos;
					for (; frm_ != to; ++frm_) {
						auto slot = (frm_->item ^ rnd) % N;
						if (_items[slot] || slot == used_zero) {
							break;
						}
						if (frm_->item) {
							_items[slot] = frm_->item;
						} else {
							used_zero = slot;
						}
						frm_->slot = slot;
					}
					if (frm_ == to) {
						index = rnd;
						frm = frm_;
						break;
					}
					// it failed to place all items in empty slots, rollback
					for (auto frm__ = frm; frm__ != frm_; ++frm__) {
						_items[frm__->slot] = 0;
					}
				}
			}
		} while (to != end);
	}

	constexpr std::size_t find(const T& item) const {
		auto slot = static_cast<std::size_t>((item ^ _index[item % N]) % N);
		return _items[slot] == item ? slot : npos;
	}

	constexpr std::size_t operator[](const T& item) const {
		auto slot = find(item);
		if (slot == npos) {
			throw std::out_of_range("Item not found");
		}
		return slot;
	}

	constexpr auto size() const {
		return N;
	}
};

template <typename T, std::size_t N>
constexpr static auto
init(const T (&items)[N]) {
	return mph<T, N>(items);
}

} // namespace mph

#define MPH_HASH(arg) fnv1ah32::hash(arg)
#define MPH_VAR(name) mph_##name
#define MPH_FIND(arg, name) MPH_VAR(name).find(MPH_HASH(arg))

#define MPH_INIT_BEGIN(name) static constexpr auto MPH_VAR(name) = mph::init({
#define MPH_OPTION_INIT(option, arg) MPH_HASH(#option),
#define MPH_INIT_END(name) });

#define MPH_SWITCH_BEGIN(arg, name) switch (MPH_FIND(arg, name)) {
#define MPH_OPTION_CASE(option, name) case MPH_FIND(#option, name)
#define MPH_OPTION_CASE_RETURN_STRING(option, name) MPH_OPTION_CASE(option, name): { static const std::string _(#option); return _; }
#define MPH_OPTION_CASE_DISPATCH(option, name, ...) MPH_OPTION_CASE(option, name): return _##name##_dispatcher_ ##option(__VA_ARGS__);
#define MPH_SWITCH_END(arg) }

#endif // MPH_HH
<commit_msg>Renamed init -> make_mph<commit_after>/*
 * Copyright (C) 2018 German Mendez Bravo (Kronuz)
 *
 * 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.
 */

#ifndef MPH_HH
#define MPH_HH

#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>

namespace mph {

////////////////////////////////////////////////////////////////////////////////
// Constexpr linear congruential random number generator

template <typename T, T multiplier, T increment, T modulus>
class linear_congruential_generator {
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

	T seed = 1u;

public:
	constexpr T operator()() {
		seed = (seed * multiplier + increment) % modulus;
		return seed;
	}
};

////////////////////////////////////////////////////////////////////////////////
// Constexpr quicksort

template <class T>
constexpr void swap(T& a, T& b) {
	auto tmp = a;
	a = b;
	b = tmp;
}

template <typename It>
constexpr It partition(It left, It right) {
	auto pivot = left + (right - left) / 2;
	auto value = *pivot;
	swap(*right, *pivot);
	for (auto it = left; it < right; ++it) {
		if (*it < value) {
			swap(*it, *left);
			++left;
		}
	}
	swap(*right, *left);
	return left;
}

template <typename It>
constexpr void quicksort(It left, It right) {
	if (left < right) {
		auto pivot = partition(left, right);
		quicksort(left, pivot);
		quicksort(pivot + 1, right);
	}
}

////////////////////////////////////////////////////////////////////////////////
// Computes a constexpr minimal perfect hash table

constexpr static auto npos = std::numeric_limits<std::size_t>::max();

#define MPH_SORT_CLASHES

template <typename T, std::size_t N, typename RNG = linear_congruential_generator<T, 48271, 0, 2147483647>>
class mph {
	static_assert(N > 0, "Must have at least one element");
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

	struct hashed_item_t {
		T item;
		std::size_t slot;
		std::size_t cnt;

		constexpr hashed_item_t() : item{0}, slot{0}, cnt{0} { }

		constexpr hashed_item_t(const hashed_item_t& other) :
			item{other.item},
			slot{other.slot},
			cnt{other.cnt} { }

		constexpr hashed_item_t(hashed_item_t&& other) noexcept :
			item{std::move(other.item)},
			slot{std::move(other.slot)},
			cnt{std::move(other.cnt)} { }

		constexpr hashed_item_t& operator=(const hashed_item_t& other) {
			item = other.item;
			slot = other.slot;
			cnt = other.cnt;
			return *this;
		}

		constexpr hashed_item_t& operator=(hashed_item_t&& other) noexcept {
			item = std::move(other.item);
			slot = std::move(other.slot);
			cnt = std::move(other.cnt);
			return *this;
		}

		constexpr bool operator<(const hashed_item_t& other) const {
			if (cnt == other.cnt) {
				return slot < other.slot;
			}
			return cnt > other.cnt;
		}
	};

	T _index[N];
	T _items[N];

public:
	constexpr mph(const T (&items)[N]) : _index{}, _items{} {
		RNG rng;
		hashed_item_t hashed_items[N];

		for (std::size_t pos = 0; pos < N; ++pos) {
			auto& item = items[pos];
			auto& hashed_item = hashed_items[pos];
			auto hashed = item;
			hashed_item.item = item;
			hashed_item.slot = static_cast<std::size_t>(hashed % N);
		}

		quicksort(&hashed_items[0], &hashed_items[N - 1]);

		auto end = &hashed_items[N];
		auto frm = &hashed_items[0];
		auto to = frm;

#ifdef MPH_SORT_CLASHES
		do {
			++to;
			if (to == end || frm->slot != to->slot) {
				auto cnt = to - frm;
				for (; frm != to; ++frm) {
					frm->cnt = cnt;
				}
			}
		} while (to != end);

		quicksort(&hashed_items[0], &hashed_items[N - 1]);

		frm = &hashed_items[0];
		to = frm;
#endif

		do {
			++to;
			if (to == end || frm->slot != to->slot) {
				auto& index = _index[frm->slot];
				while (true) {
					auto rnd = rng();
					if (!rnd) {
						continue;
					}
					auto frm_ = frm;
					std::size_t used_zero = npos;
					for (; frm_ != to; ++frm_) {
						auto slot = (frm_->item ^ rnd) % N;
						if (_items[slot] || slot == used_zero) {
							break;
						}
						if (frm_->item) {
							_items[slot] = frm_->item;
						} else {
							used_zero = slot;
						}
						frm_->slot = slot;
					}
					if (frm_ == to) {
						index = rnd;
						frm = frm_;
						break;
					}
					// it failed to place all items in empty slots, rollback
					for (auto frm__ = frm; frm__ != frm_; ++frm__) {
						_items[frm__->slot] = 0;
					}
				}
			}
		} while (to != end);
	}

	constexpr std::size_t find(const T& item) const {
		auto slot = static_cast<std::size_t>((item ^ _index[item % N]) % N);
		return _items[slot] == item ? slot : npos;
	}

	constexpr std::size_t operator[](const T& item) const {
		auto slot = find(item);
		if (slot == npos) {
			throw std::out_of_range("Item not found");
		}
		return slot;
	}

	constexpr auto size() const {
		return N;
	}
};

template <typename T, std::size_t N>
constexpr static auto
make_mph(const T (&items)[N]) {
	return mph<T, N>(items);
}

} // namespace mph

#define MPH_HASH(arg) fnv1ah32::hash(arg)
#define MPH_VAR(name) mph_##name
#define MPH_FIND(arg, name) MPH_VAR(name).find(MPH_HASH(arg))

#define MPH_INIT_BEGIN(name) static constexpr auto MPH_VAR(name) = mph::make_mph({
#define MPH_OPTION_INIT(option, arg) MPH_HASH(#option),
#define MPH_INIT_END(name) });

#define MPH_SWITCH_BEGIN(arg, name) switch (MPH_FIND(arg, name)) {
#define MPH_OPTION_CASE(option, name) case MPH_FIND(#option, name)
#define MPH_OPTION_CASE_RETURN_STRING(option, name) MPH_OPTION_CASE(option, name): { static const std::string _(#option); return _; }
#define MPH_OPTION_CASE_DISPATCH(option, name, ...) MPH_OPTION_CASE(option, name): return _##name##_dispatcher_ ##option(__VA_ARGS__);
#define MPH_SWITCH_END(arg) }

#endif // MPH_HH
<|endoftext|>


<commit_before>#include "pp.hpp"
#include "lexer.hpp"
#include "parse.hpp"

Token Preprocessor::run(Token tok) {
  token = tok;
  while(token.get().type != TOK_TYPE_END) {
    if(token.skip("#")) {
           if(token.skip("include"))             read_include();
      else if(token.skip("define"))              read_define();
      else if(token.skip("undef"))               read_undef();
      else if(token.skip("if"))                  read_if();
      else if(token.skip("ifdef"))               read_ifdef();
      else if(token.skip("ifndef"))              read_ifndef();
      else if(token.skip("else"))                read_else();
      else                                       skip_this_line();
    } else if(define_map.count(token.get().val)) replace_macro();
    else new_token.token.push_back(token.next());
  }
  token.pos = 0;
  return new_token;
}

void Preprocessor::read_define() {
  std::string macro_name; bool funclike = false;
  std::vector<std::string> args_name;
  int cur_line = token.get().line;
  if(token.get().type == TOK_TYPE_IDENT)
    macro_name = token.next().val;
  if(token.is("(") && !token.get().space) {
    token.skip();
    funclike = true;
    while(1) {
      args_name.push_back(token.next().val);
      if(token.skip(")")) break;
      token.expect_skip(",");
    }
  }
  std::vector<token_t> content;
  if(cur_line == token.get().line) {
    while(cur_line==token.get().line)
      content.push_back(token.next());
  }
  if(funclike) add_define_funclike_macro(macro_name, args_name, content);
  else add_define_macro(macro_name, content);
}

void Preprocessor::read_undef() {
  std::string macro;
  if(token.get().type == TOK_TYPE_IDENT)
    macro = token.next().val;
  auto it = define_map.find(macro);
  if(it != define_map.end()) // if macro was declared
    define_map.erase(it);
}

token_t Preprocessor::read_defined_op() {
  std::string macro_name;
  if(token.skip("(")) {
    macro_name = token.next().val;
    token.expect_skip(")");
  } else {
    macro_name = token.next().val;
  }
  std::cout << this->define_map.count(macro_name) << std::endl;;
  return this->define_map.count(macro_name) ?
    token_t(TOK_TYPE_NUMBER, "1", token.get().line) :
    token_t(TOK_TYPE_NUMBER, "0", token.get().line);
}

Token Preprocessor::read_expr_line() {
  Token expr_line;
  int cur_line = token.get().line;
  for(;;) {
    if(cur_line != token.get().line) break;
    if(token.skip("defined")) {
      expr_line.token.push_back(read_defined_op());
    } else {
      expr_line.token.push_back(token.next());
    }
  }
  return expr_line;
}

bool Preprocessor::read_constexpr() {
  // TODO: implement processing of const expression
  auto tok = read_expr_line();
  tok.add_symbol_tok(";", 0);
  tok.add_end_tok();
  tok.show();
  tok.pos = 0;
  Parser parser;
  auto expr = parser.run(tok, true)[0];
  bool cond = eval_constexpr(expr);
  return cond;
}

void Preprocessor::do_read_if(bool m) {
  cond_stack.push(m);
  if(!m) skip_cond_include();
}

void Preprocessor::read_if() {
  do_read_if(read_constexpr());
}

void Preprocessor::read_ifdef() {
  if(token.get().type != TOK_TYPE_IDENT) error("error: in pp");
  std::string macro = token.next().val;
  do_read_if( define_map.count(macro) );
}

void Preprocessor::read_ifndef() {
  if(token.get().type != TOK_TYPE_IDENT) error("error: in pp");
  std::string macro = token.next().val;
  do_read_if( !define_map.count(macro) );
}

void Preprocessor::read_else() {
  if(cond_stack.empty()) error("error: in pp");
  if(cond_stack.top()) // if immediately before cond is true
    skip_cond_include();
}

void Preprocessor::skip_cond_include() {
  int nest = 0;
  for(;;) {
    if(!token.skip("#")) { token.skip(); continue; }
    if(!nest && (token.skip("else") || token.skip("elif") || token.skip("endif")))
      return;
    if(token.skip("if") || token.skip("ifdef") || token.skip("ifndef"))
      nest++;
    else if(nest && token.skip("endif"))
      nest--;
  }
}

void Preprocessor::read_include() {
  std::string file_name;
  if(token.skip("<")) {
    while(!token.skip(">")) file_name += token.next().val;
  } else if(token.get().type == TOK_TYPE_STRING) {
    file_name = token.next().val;
  }
  std::string include_content = [&]() -> std::string {
    std::ifstream ifs_src(default_include_path + file_name);
    if(!ifs_src) { puts("file not found"); return ""; }
    std::istreambuf_iterator<char> it(ifs_src), last;
    std::string src_all(it, last);
    return src_all;
  }();
  Lexer lex; Token include_tok = lex.run(include_content);
  Preprocessor pp; include_tok = pp.run(include_tok);
  for(auto incl_def : pp.define_map)
    define_map[incl_def.first] = incl_def.second;
  for(auto t : include_tok.token)
    new_token.token.push_back(t);
}
void Preprocessor::replace_macro() {
  auto macro = define_map[token.get().val];
  // normal macro
  if(macro.type == DEFINE_MACRO) {
    std::copy(macro.rep.begin(), macro.rep.end(), std::back_inserter(new_token.token));
    token.skip();
    return;
  }

  // funclike macro
  token.skip(); // IDENT
  token.expect_skip("(");
  std::vector< std::vector<token_t> > args;
  while(token.get().type != TOK_TYPE_END) {
    std::vector<token_t> arg;
    int nest = 1;
    while(1) {
      if(token.is("(")) nest++; if(token.is(")")) nest--;
      if((token.is(")") && !nest) || token.is(",")) break;
      arg.push_back(token.next());
    }
    args.push_back(arg);
    if(token.skip(")") && !nest) break;
    token.expect_skip(",");
  }
  auto repd_end = token.pos;
  std::vector<token_t> rep_to = macro.rep;
  for(int i = 0; i < macro.args.size(); i++) {
    auto it = std::find_if(rep_to.begin(), rep_to.end(), [&](token_t &t) {
        return t.val == macro.args[i]; });
    while(it != rep_to.end()) {

      if(*(it-1) && (it-1)->val == "#") {
        rep_to.erase(it-1); rep_to.erase(it);
        std::string str;
        for(auto s : args[i])
          str += s.val;
        auto new_str = token_t(TOK_TYPE_STRING, str);
        rep_to.insert(it-1, new_str);
      } else {
        rep_to.erase(it);
        rep_to.insert(it, args[i].begin(), args[i].end());
      }
      it = std::find_if(rep_to.begin(), rep_to.end(), [&](token_t &t) {
          return t.val == macro.args[i]; });
    }
  }
  std::copy(rep_to.begin(), rep_to.end(), std::back_inserter(new_token.token));
}

void Preprocessor::skip_this_line() {
  int n = token.get().line;
  while(n == token.get().line) token.skip();
}

void Preprocessor::add_define_macro(std::string name, std::vector<token_t> rep) {
  define_t def;
  def.type = DEFINE_MACRO;
  def.rep = rep;
  define_map[name] = def;
}
void Preprocessor::add_define_funclike_macro(std::string name, std::vector<std::string> args_name, std::vector<token_t> rep) {
  define_t def;
  def.type = DEFINE_FUNCLIKE_MACRO;
  def.args = args_name;
  def.rep = rep;
  define_map[name] = def;
}
<commit_msg>Update pp.cpp<commit_after>#include "pp.hpp"
#include "lexer.hpp"
#include "parse.hpp"

Token Preprocessor::run(Token tok) {
  token = tok;
  while(token.get().type != TOK_TYPE_END) {
    if(token.skip("#")) {
           if(token.skip("include"))             read_include();
      else if(token.skip("define"))              read_define();
      else if(token.skip("undef"))               read_undef();
      else if(token.skip("if"))                  read_if();
      else if(token.skip("ifdef"))               read_ifdef();
      else if(token.skip("ifndef"))              read_ifndef();
      else if(token.skip("else"))                read_else();
      else                                       skip_this_line();
    } else if(define_map.count(token.get().val)) replace_macro();
    else new_token.token.push_back(token.next());
  }
  token.pos = 0;
  return new_token;
}

void Preprocessor::read_define() {
  std::string macro_name; bool funclike = false;
  std::vector<std::string> args_name;
  int cur_line = token.get().line;
  if(token.get().type == TOK_TYPE_IDENT)
    macro_name = token.next().val;
  if(token.is("(") && !token.get().space) {
    token.skip();
    funclike = true;
    while(1) {
      args_name.push_back(token.next().val);
      if(token.skip(")")) break;
      token.expect_skip(",");
    }
  }
  std::vector<token_t> content;
  if(cur_line == token.get().line) {
    while(cur_line==token.get().line)
      content.push_back(token.next());
  }
  if(funclike) add_define_funclike_macro(macro_name, args_name, content);
  else add_define_macro(macro_name, content);
}

void Preprocessor::read_undef() {
  std::string macro;
  if(token.get().type == TOK_TYPE_IDENT)
    macro = token.next().val;
  auto it = define_map.find(macro);
  if(it != define_map.end()) // if macro was declared
    define_map.erase(it);
}

token_t Preprocessor::read_defined_op() {
  std::string macro_name;
  if(token.skip("(")) {
    macro_name = token.next().val;
    token.expect_skip(")");
  } else {
    macro_name = token.next().val;
  }
  std::cout << this->define_map.count(macro_name) << std::endl;;
  return this->define_map.count(macro_name) ?
    token_t(TOK_TYPE_NUMBER, "1", token.get().line) :
    token_t(TOK_TYPE_NUMBER, "0", token.get().line);
}

Token Preprocessor::read_expr_line() {
  Token expr_line;
  int cur_line = token.get().line;
  for(;;) {
    if(cur_line != token.get().line) break;
    if(token.skip("defined")) {
      expr_line.token.push_back(read_defined_op());
    } else {
      expr_line.token.push_back(token.next());
    }
  }
  return expr_line;
}

bool Preprocessor::read_constexpr() {
  // TODO: implement processing of const expression
  auto tok = read_expr_line();
  tok.add_symbol_tok(";", 0);
  tok.add_end_tok();
  tok.show();
  tok.pos = 0;
  Parser parser;
  auto expr = parser.run(tok, true)[0];
  bool cond = eval_constexpr(expr);
  return cond;
}

void Preprocessor::do_read_if(bool m) {
  cond_stack.push(m);
  if(!m) skip_cond_include();
}

void Preprocessor::read_if() {
  do_read_if(read_constexpr());
}

void Preprocessor::read_ifdef() {
  if(token.get().type != TOK_TYPE_IDENT) error("error: in pp");
  std::string macro = token.next().val;
  do_read_if( define_map.count(macro) );
}

void Preprocessor::read_ifndef() {
  if(token.get().type != TOK_TYPE_IDENT) error("error: in pp");
  std::string macro = token.next().val;
  do_read_if( !define_map.count(macro) );
}

void Preprocessor::read_else() {
  if(cond_stack.empty()) error("error: in pp");
  if(cond_stack.top()) // if immediately before cond is true
    skip_cond_include();
}

void Preprocessor::skip_cond_include() {
  int nest = 0;
  for(;;) {
    if(!token.skip("#")) { token.skip(); continue; }
    if(!nest && (token.skip("else") || token.skip("elif") || token.skip("endif")))
      return;
    if(token.skip("if") || token.skip("ifdef") || token.skip("ifndef"))
      nest++;
    else if(nest && token.skip("endif"))
      nest--;
  }
}

void Preprocessor::read_include() {
  std::string file_name;
  if(token.skip("<")) {
    while(!token.skip(">")) file_name += token.next().val;
  } else if(token.get().type == TOK_TYPE_STRING) {
    file_name = token.next().val;
  }
  std::string include_content = [&]() -> std::string {
    std::ifstream ifs_src(default_include_path + file_name);
    if(!ifs_src) { puts("file not found"); return ""; }
    std::istreambuf_iterator<char> it(ifs_src), last;
    std::string src_all(it, last);
    return src_all;
  }();
  Lexer lex; Token include_tok = lex.run(include_content);
  Preprocessor pp; include_tok = pp.run(include_tok);
  for(auto incl_def : pp.define_map)
    define_map[incl_def.first] = incl_def.second;
  for(auto t : include_tok.token)
    new_token.token.push_back(t);
}
void Preprocessor::replace_macro() {
  auto macro = define_map[token.get().val];
  // normal macro
  if(macro.type == DEFINE_MACRO) {
    std::copy(macro.rep.begin(), macro.rep.end(), std::back_inserter(new_token.token));
    token.skip();
    return;
  }

  // funclike macro
  token.skip(); // IDENT
  token.expect_skip("(");
  std::vector< std::vector<token_t> > args;
  while(token.get().type != TOK_TYPE_END) {
    std::vector<token_t> arg;
    int nest = 1;
    while(1) {
      if(token.is("(")) nest++; if(token.is(")")) nest--;
      if((token.is(")") && !nest) || token.is(",")) break;
      arg.push_back(token.next());
    }
    args.push_back(arg);
    if(token.skip(")") && !nest) break;
    token.expect_skip(",");
  }
  auto repd_end = token.pos;
  std::vector<token_t> rep_to = macro.rep;
  for(int i = 0; i < macro.args.size(); i++) {
    auto it = std::find_if(rep_to.begin(), rep_to.end(), [&](token_t &t) {
        return t.val == macro.args[i]; });
    while(it != rep_to.end()) {

      if(it != rep_to.begin() && (it-1)->val == "#") {
        rep_to.erase(it-1); rep_to.erase(it);
        std::string str;
        for(auto s : args[i])
          str += s.val;
        auto new_str = token_t(TOK_TYPE_STRING, str);
        rep_to.insert(it-1, new_str);
      } else {
        rep_to.erase(it);
        rep_to.insert(it, args[i].begin(), args[i].end());
      }
      it = std::find_if(rep_to.begin(), rep_to.end(), [&](token_t &t) {
          return t.val == macro.args[i]; });
    }
  }
  std::copy(rep_to.begin(), rep_to.end(), std::back_inserter(new_token.token));
}

void Preprocessor::skip_this_line() {
  int n = token.get().line;
  while(n == token.get().line) token.skip();
}

void Preprocessor::add_define_macro(std::string name, std::vector<token_t> rep) {
  define_t def;
  def.type = DEFINE_MACRO;
  def.rep = rep;
  define_map[name] = def;
}
void Preprocessor::add_define_funclike_macro(std::string name, std::vector<std::string> args_name, std::vector<token_t> rep) {
  define_t def;
  def.type = DEFINE_FUNCLIKE_MACRO;
  def.args = args_name;
  def.rep = rep;
  define_map[name] = def;
}
<|endoftext|>


<commit_before>/**
 * pty.js
 * Copyright (c) 2012, Christopher Jeffrey (MIT License)
 *
 * pty.cc:
 *   This file is responsible for starting processes
 *   with pseudo-terminal file descriptors.
 *
 * See:
 *   man pty
 *   man tty_ioctl
 *   man tcsetattr
 *   man forkpty
 */

#include <v8.h>
#include <node.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>

/* forkpty */
/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */
#if defined(__GLIBC__) || defined(__CYGWIN__)
#include <pty.h>
#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <util.h>
#elif defined(__FreeBSD__)
#include <libutil.h>
#else
#include <pty.h>
#endif

#include <utmp.h> /* login_tty */
#include <termios.h> /* tcgetattr, tty_ioctl */

/* environ for execvpe */
#if defined(__APPLE__)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif

/* for pty_getproc */
#if defined(__GLIBC__)
#include <stdio.h>
#include <stdint.h>
#elif defined(__APPLE__)
#include <sys/sysctl.h>
#include <libproc.h>
#endif

using namespace std;
using namespace node;
using namespace v8;

static Handle<Value>
PtyFork(const Arguments&);

static Handle<Value>
PtyResize(const Arguments&);

static Handle<Value>
PtyGetProc(const Arguments&);

static int
pty_execvpe(const char *, char **, char **);

static int
pty_nonblock(int);

static char *
pty_getproc(int, char *);

extern "C" void
init(Handle<Object>);

/**
 * PtyFork
 */

static Handle<Value>
PtyFork(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 6) {
    return ThrowException(Exception::Error(
      String::New("Not enough arguments.")));
  }

  if (!args[0]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("file must be a string.")));
  }

  if (!args[1]->IsArray()) {
    return ThrowException(Exception::Error(
      String::New("args must be an array.")));
  }

  if (!args[2]->IsArray()) {
    return ThrowException(Exception::Error(
      String::New("env must be an array.")));
  }

  if (!args[3]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("cwd must be a string.")));
  }

  if (!args[4]->IsNumber() || !args[5]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("cols and rows must be numbers.")));
  }

  // node/src/node_child_process.cc

  // file
  String::Utf8Value file(args[0]->ToString());

  // args
  int i = 0;
  Local<Array> argv_ = Local<Array>::Cast(args[1]);
  int argc = argv_->Length();
  int argl = argc + 1 + 1;
  char **argv = new char*[argl];
  argv[0] = strdup(*file);
  argv[argl-1] = NULL;
  for (; i < argc; i++) {
    String::Utf8Value arg(argv_->Get(Integer::New(i))->ToString());
    argv[i+1] = strdup(*arg);
  }

  // env
  i = 0;
  Local<Array> env_ = Local<Array>::Cast(args[2]);
  int envc = env_->Length();
  char **env = new char*[envc+1];
  env[envc] = NULL;
  for (; i < envc; i++) {
    String::Utf8Value pair(env_->Get(Integer::New(i))->ToString());
    env[i] = strdup(*pair);
  }

  // cwd
  String::Utf8Value cwd_(args[3]->ToString());
  char *cwd = strdup(*cwd_);

  // size
  struct winsize winp = {};
  Local<Integer> cols = args[4]->ToInteger();
  Local<Integer> rows = args[5]->ToInteger();
  winp.ws_col = cols->Value();
  winp.ws_row = rows->Value();

  // fork the pty
  int master;
  char name[40];
  pid_t pid = forkpty(&master, name, NULL, &winp);

  if (pid) {
    for (i = 0; i < argl; i++) free(argv[i]);
    delete[] argv;
    for (i = 0; i < envc; i++) free(env[i]);
    delete[] env;
    free(cwd);
  }

  switch (pid) {
    case -1:
      return ThrowException(Exception::Error(
        String::New("forkpty failed.")));
    case 0:
      if (strlen(cwd)) chdir(cwd);

      pty_execvpe(argv[0], argv, env);

      perror("execvp failed");
      _exit(1);
    default:
      // nonblocking
      if (pty_nonblock(master) == -1) {
        return ThrowException(Exception::Error(
          String::New("Could not set master fd to nonblocking.")));
      }

      Local<Object> obj = Object::New();
      obj->Set(String::New("fd"), Number::New(master));
      obj->Set(String::New("pid"), Number::New(pid));
      obj->Set(String::New("pty"), String::New(name));

      return scope.Close(obj);
  }

  return Undefined();
}

/**
 * Resize Functionality
 */

static Handle<Value>
PtyResize(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 1 || !args[0]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("First argument must be a number.")));
  }

  struct winsize winp = {};
  winp.ws_col = 80;
  winp.ws_row = 30;

  int fd = args[0]->ToInteger()->Value();

  if (args.Length() == 3) {
    if (args[1]->IsNumber() && args[2]->IsNumber()) {
      Local<Integer> cols = args[1]->ToInteger();
      Local<Integer> rows = args[2]->ToInteger();

      winp.ws_col = cols->Value();
      winp.ws_row = rows->Value();
    } else {
      return ThrowException(Exception::Error(
        String::New("cols and rows need to be numbers.")));
    }
  }

  if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
    return ThrowException(Exception::Error(
      String::New("ioctl failed.")));
  }

  return Undefined();
}

/**
 * Foreground Process Name
 */

static Handle<Value>
PtyGetProc(const Arguments& args) {
  HandleScope scope;

  if (args.Length() != 2) {
    return ThrowException(Exception::Error(
      String::New("Bad arguments.")));
  }

  int fd = args[0]->ToInteger()->Value();

  String::Utf8Value tty_(args[1]->ToString());
  char *tty = strdup(*tty_);

  char *name = pty_getproc(fd, tty);

  if (name == NULL) {
    return Undefined();
  }

  return scope.Close(String::New(name));
}

/**
 * execvpe
 */

// execvpe(3) is not portable.
// http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html
static int
pty_execvpe(const char *file, char **argv, char **envp) {
  char **old = environ;
  environ = envp;
  int ret = execvp(file, argv);
  environ = old;
  return ret;
}

/**
 * Nonblocking FD
 */

static int
pty_nonblock(int fd) {
  int flags = fcntl(fd, F_GETFL, 0);
  if (flags == -1) return -1;
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

/**
 * pty_getproc
 * Taken from tmux.
 */

// Taken from: tmux (http://tmux.sourceforge.net/)
// Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#if defined(__GLIBC__)

static char *
pty_getproc(int fd, char *tty) {
  FILE *f;
  char *path, *buf;
  size_t len;
  int ch;
  pid_t pgrp;
  int r;

  if ((pgrp = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp);
  if (r == -1 || path == NULL) return NULL;

  if ((f = fopen(path, "r")) == NULL) {
    free(path);
    return NULL;
  }

  free(path);

  len = 0;
  buf = NULL;
  while ((ch = fgetc(f)) != EOF) {
    if (ch == '\0') break;
    //if (SIZE_MAX < len + 2) return NULL;
    buf = (char *)realloc(buf, len + 2);
    if (buf == NULL) return NULL;
    buf[len++] = ch;
  }

  if (buf != NULL) {
    buf[len] = '\0';
  }

  fclose(f);
  return buf;
}

#elif defined(__APPLE__)

static char *
pty_getproc(int fd, char *tty) {
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
  size_t size;
  struct kinfo_proc kp;

  if ((mib[3] = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  size = sizeof kp;
  if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
    return NULL;
  }

  if (*kp.kp_proc.p_comm == '\0') {
    return NULL;
  }

  return strdup(kp.kp_proc.p_comm);
}

#else

static char *
pty_getproc(int fd, char *tty) {
  return NULL;
}

#endif

/**
 * Init
 */

extern "C" void
init(Handle<Object> target) {
  HandleScope scope;
  NODE_SET_METHOD(target, "fork", PtyFork);
  NODE_SET_METHOD(target, "resize", PtyResize);
  NODE_SET_METHOD(target, "process", PtyGetProc);
}
<commit_msg>fix tmux license<commit_after>/**
 * pty.js
 * Copyright (c) 2012, Christopher Jeffrey (MIT License)
 *
 * pty.cc:
 *   This file is responsible for starting processes
 *   with pseudo-terminal file descriptors.
 *
 * See:
 *   man pty
 *   man tty_ioctl
 *   man tcsetattr
 *   man forkpty
 */

#include <v8.h>
#include <node.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>

/* forkpty */
/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */
#if defined(__GLIBC__) || defined(__CYGWIN__)
#include <pty.h>
#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <util.h>
#elif defined(__FreeBSD__)
#include <libutil.h>
#else
#include <pty.h>
#endif

#include <utmp.h> /* login_tty */
#include <termios.h> /* tcgetattr, tty_ioctl */

/* environ for execvpe */
#if defined(__APPLE__)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif

/* for pty_getproc */
#if defined(__GLIBC__)
#include <stdio.h>
#include <stdint.h>
#elif defined(__APPLE__)
#include <sys/sysctl.h>
#include <libproc.h>
#endif

using namespace std;
using namespace node;
using namespace v8;

static Handle<Value>
PtyFork(const Arguments&);

static Handle<Value>
PtyResize(const Arguments&);

static Handle<Value>
PtyGetProc(const Arguments&);

static int
pty_execvpe(const char *, char **, char **);

static int
pty_nonblock(int);

static char *
pty_getproc(int, char *);

extern "C" void
init(Handle<Object>);

/**
 * PtyFork
 */

static Handle<Value>
PtyFork(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 6) {
    return ThrowException(Exception::Error(
      String::New("Not enough arguments.")));
  }

  if (!args[0]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("file must be a string.")));
  }

  if (!args[1]->IsArray()) {
    return ThrowException(Exception::Error(
      String::New("args must be an array.")));
  }

  if (!args[2]->IsArray()) {
    return ThrowException(Exception::Error(
      String::New("env must be an array.")));
  }

  if (!args[3]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("cwd must be a string.")));
  }

  if (!args[4]->IsNumber() || !args[5]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("cols and rows must be numbers.")));
  }

  // node/src/node_child_process.cc

  // file
  String::Utf8Value file(args[0]->ToString());

  // args
  int i = 0;
  Local<Array> argv_ = Local<Array>::Cast(args[1]);
  int argc = argv_->Length();
  int argl = argc + 1 + 1;
  char **argv = new char*[argl];
  argv[0] = strdup(*file);
  argv[argl-1] = NULL;
  for (; i < argc; i++) {
    String::Utf8Value arg(argv_->Get(Integer::New(i))->ToString());
    argv[i+1] = strdup(*arg);
  }

  // env
  i = 0;
  Local<Array> env_ = Local<Array>::Cast(args[2]);
  int envc = env_->Length();
  char **env = new char*[envc+1];
  env[envc] = NULL;
  for (; i < envc; i++) {
    String::Utf8Value pair(env_->Get(Integer::New(i))->ToString());
    env[i] = strdup(*pair);
  }

  // cwd
  String::Utf8Value cwd_(args[3]->ToString());
  char *cwd = strdup(*cwd_);

  // size
  struct winsize winp = {};
  Local<Integer> cols = args[4]->ToInteger();
  Local<Integer> rows = args[5]->ToInteger();
  winp.ws_col = cols->Value();
  winp.ws_row = rows->Value();

  // fork the pty
  int master;
  char name[40];
  pid_t pid = forkpty(&master, name, NULL, &winp);

  if (pid) {
    for (i = 0; i < argl; i++) free(argv[i]);
    delete[] argv;
    for (i = 0; i < envc; i++) free(env[i]);
    delete[] env;
    free(cwd);
  }

  switch (pid) {
    case -1:
      return ThrowException(Exception::Error(
        String::New("forkpty failed.")));
    case 0:
      if (strlen(cwd)) chdir(cwd);

      pty_execvpe(argv[0], argv, env);

      perror("execvp failed");
      _exit(1);
    default:
      // nonblocking
      if (pty_nonblock(master) == -1) {
        return ThrowException(Exception::Error(
          String::New("Could not set master fd to nonblocking.")));
      }

      Local<Object> obj = Object::New();
      obj->Set(String::New("fd"), Number::New(master));
      obj->Set(String::New("pid"), Number::New(pid));
      obj->Set(String::New("pty"), String::New(name));

      return scope.Close(obj);
  }

  return Undefined();
}

/**
 * Resize Functionality
 */

static Handle<Value>
PtyResize(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 1 || !args[0]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("First argument must be a number.")));
  }

  struct winsize winp = {};
  winp.ws_col = 80;
  winp.ws_row = 30;

  int fd = args[0]->ToInteger()->Value();

  if (args.Length() == 3) {
    if (args[1]->IsNumber() && args[2]->IsNumber()) {
      Local<Integer> cols = args[1]->ToInteger();
      Local<Integer> rows = args[2]->ToInteger();

      winp.ws_col = cols->Value();
      winp.ws_row = rows->Value();
    } else {
      return ThrowException(Exception::Error(
        String::New("cols and rows need to be numbers.")));
    }
  }

  if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
    return ThrowException(Exception::Error(
      String::New("ioctl failed.")));
  }

  return Undefined();
}

/**
 * Foreground Process Name
 */

static Handle<Value>
PtyGetProc(const Arguments& args) {
  HandleScope scope;

  if (args.Length() != 2) {
    return ThrowException(Exception::Error(
      String::New("Bad arguments.")));
  }

  int fd = args[0]->ToInteger()->Value();

  String::Utf8Value tty_(args[1]->ToString());
  char *tty = strdup(*tty_);

  char *name = pty_getproc(fd, tty);

  if (name == NULL) {
    return Undefined();
  }

  return scope.Close(String::New(name));
}

/**
 * execvpe
 */

// execvpe(3) is not portable.
// http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html
static int
pty_execvpe(const char *file, char **argv, char **envp) {
  char **old = environ;
  environ = envp;
  int ret = execvp(file, argv);
  environ = old;
  return ret;
}

/**
 * Nonblocking FD
 */

static int
pty_nonblock(int fd) {
  int flags = fcntl(fd, F_GETFL, 0);
  if (flags == -1) return -1;
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

/**
 * pty_getproc
 * Taken from tmux.
 */

// Taken from: tmux (http://tmux.sourceforge.net/)
// Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
// Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#if defined(__GLIBC__)

static char *
pty_getproc(int fd, char *tty) {
  FILE *f;
  char *path, *buf;
  size_t len;
  int ch;
  pid_t pgrp;
  int r;

  if ((pgrp = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp);
  if (r == -1 || path == NULL) return NULL;

  if ((f = fopen(path, "r")) == NULL) {
    free(path);
    return NULL;
  }

  free(path);

  len = 0;
  buf = NULL;
  while ((ch = fgetc(f)) != EOF) {
    if (ch == '\0') break;
    //if (SIZE_MAX < len + 2) return NULL;
    buf = (char *)realloc(buf, len + 2);
    if (buf == NULL) return NULL;
    buf[len++] = ch;
  }

  if (buf != NULL) {
    buf[len] = '\0';
  }

  fclose(f);
  return buf;
}

#elif defined(__APPLE__)

static char *
pty_getproc(int fd, char *tty) {
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
  size_t size;
  struct kinfo_proc kp;

  if ((mib[3] = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  size = sizeof kp;
  if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
    return NULL;
  }

  if (*kp.kp_proc.p_comm == '\0') {
    return NULL;
  }

  return strdup(kp.kp_proc.p_comm);
}

#else

static char *
pty_getproc(int fd, char *tty) {
  return NULL;
}

#endif

/**
 * Init
 */

extern "C" void
init(Handle<Object> target) {
  HandleScope scope;
  NODE_SET_METHOD(target, "fork", PtyFork);
  NODE_SET_METHOD(target, "resize", PtyResize);
  NODE_SET_METHOD(target, "process", PtyGetProc);
}
<|endoftext|>


<commit_before>#include "common.hpp"
#include <string.h>
#include <os.hpp>
#include <errno.h>
#include <kprint>

static uintptr_t brk_begin        = 0;
static uintptr_t brk_end          = 0;
static uintptr_t brk_initialized  = 0;
static ssize_t   brk_max          = 0;

uintptr_t __init_brk(uintptr_t begin, size_t size)
{
  brk_begin = begin;
  brk_end   = begin;
  brk_max   = begin + size;
  brk_initialized = brk_end;
  return brk_begin + brk_max;
}

size_t brk_bytes_used() {
  return brk_end - brk_begin;
}

size_t brk_bytes_free() {
  return brk_max - brk_bytes_used();
}

static uintptr_t sys_brk(void* addr)
{
  if (addr == nullptr
      or (uintptr_t)addr > brk_begin +  brk_max
      or (uintptr_t)addr < brk_begin) {
    return brk_end;
  }

  brk_end = (uintptr_t)addr;

  if (brk_end > brk_initialized) {
    memset((void*)brk_initialized, 0, brk_end - brk_initialized);
    brk_initialized = brk_end;
  }

  return brk_end;
}

extern "C"
uintptr_t syscall_SYS_brk(void* addr)
{
  return strace(sys_brk, "brk", addr);
}
<commit_msg>fix: brk_end calculation was off<commit_after>#include "common.hpp"
#include <string.h>
#include <os.hpp>
#include <errno.h>
#include <kprint>

static uintptr_t brk_begin        = 0;
static uintptr_t brk_end          = 0;
static uintptr_t brk_initialized  = 0;
static ssize_t   brk_max          = 0;

uintptr_t __init_brk(uintptr_t begin, size_t size)
{
  brk_begin = begin;
  brk_end   = begin;
  brk_max   = begin + size;
  brk_initialized = brk_end;
  return brk_max;
}

size_t brk_bytes_used() {
  return brk_end - brk_begin;
}

size_t brk_bytes_free() {
  return brk_max - brk_bytes_used();
}

static uintptr_t sys_brk(void* addr)
{
  if (addr == nullptr
      or (uintptr_t)addr > brk_begin +  brk_max
      or (uintptr_t)addr < brk_begin) {
    return brk_end;
  }

  brk_end = (uintptr_t)addr;

  if (brk_end > brk_initialized) {
    memset((void*)brk_initialized, 0, brk_end - brk_initialized);
    brk_initialized = brk_end;
  }

  return brk_end;
}

extern "C"
uintptr_t syscall_SYS_brk(void* addr)
{
  return strace(sys_brk, "brk", addr);
}
<|endoftext|>


<commit_before>#include "lm/enumerate_vocab.hh"
#include "lm/model.hh"
#include "lm/left.hh"
#include "lm/state.hh"
#include "util/murmur_hash.hh"

#include <iostream>

#include <string.h>
#include <stdlib.h>
#include <jni.h>
#include <pthread.h>

// Grr.  Everybody's compiler is slightly different and I'm trying to not depend on boost.
#include <ext/hash_map>

// This is needed to compile on OS X Lion / gcc 4.2.1
namespace __gnu_cxx {
template<>
struct hash<unsigned long long int> {
  size_t operator()(unsigned long long int __x) const {
    return __x;
  }
};
}

// Verify that jint and lm::ngram::WordIndex are the same size. If this breaks
// for you, there's a need to revise probString.
namespace {

template<bool> struct StaticCheck {
};

template<> struct StaticCheck<true> {
  typedef bool StaticAssertionPassed;
};

typedef StaticCheck<sizeof(jint) == sizeof(lm::WordIndex)>::StaticAssertionPassed FloatSize;

// Vocab ids above what the vocabulary knows about are unknown and should
// be mapped to that.
void MapArray(const std::vector<lm::WordIndex>& map, jint *begin, jint *end) {
  for (jint *i = begin; i < end; ++i) {
    *i = map[*i];
  }
}

char *PieceCopy(const StringPiece &str) {
  char *ret = (char*) malloc(str.size() + 1);
  memcpy(ret, str.data(), str.size());
  ret[str.size()] = 0;
  return ret;
}

// Rather than handle several different instantiations over JNI, we'll just
// do virtual calls C++-side.
class VirtualBase {
public:
  virtual ~VirtualBase() {
  }

  virtual float Prob(jint *begin, jint *end) const = 0;

  virtual float ProbRule(jlong *begin, jlong *end, lm::ngram::ChartState& state) const = 0;

  virtual float ProbString(jint * const begin, jint * const end,
      jint start) const = 0;

  virtual uint8_t Order() const = 0;

  virtual bool RegisterWord(const StringPiece& word, const int joshua_id) = 0;

protected:
  VirtualBase() {
  }

private:
};

template<class Model> class VirtualImpl: public VirtualBase {
public:
  VirtualImpl(const char *name) :
      m_(name) {
    // Insert unknown id mapping.
    map_.push_back(0);
  }

  ~VirtualImpl() {
  }

  float Prob(jint * const begin, jint * const end) const {
    MapArray(map_, begin, end);

    std::reverse(begin, end - 1);
    lm::ngram::State ignored;
    return m_.FullScoreForgotState(
        reinterpret_cast<const lm::WordIndex*>(begin),
        reinterpret_cast<const lm::WordIndex*>(end - 1), *(end - 1),
        ignored).prob;
  }

  float ProbRule(jlong * const begin, jlong * const end, lm::ngram::ChartState& state) const {
//    MapArray(map_, begin, end);

    lm::ngram::RuleScore<Model> ruleScore(m_, state);
    for (jlong* i = begin; i != end; i++) {
      long word = *i;
      if (word < 0)
        ruleScore.NonTerminal(*reinterpret_cast<const lm::ngram::ChartState*>(-word));
      else
        ruleScore.Terminal(word);
    }
    return ruleScore.Finish();
  }

  float ProbString(jint * const begin, jint * const end, jint start) const {
    MapArray(map_, begin, end);

    float prob;
    lm::ngram::State state;
    if (start == 0) {
      prob = 0;
      state = m_.NullContextState();
    } else {
      std::reverse(begin, begin + start);
      prob = m_.FullScoreForgotState(
          reinterpret_cast<const lm::WordIndex*>(begin),
          reinterpret_cast<const lm::WordIndex*>(begin + start),
          begin[start], state).prob;
      ++start;
    }
    lm::ngram::State state2;
    for (const jint *i = begin + start;;) {
      if (i >= end)
        break;
      float got = m_.Score(state, *i, state2);
      i++;
      prob += got;
      if (i >= end)
        break;
      got = m_.Score(state2, *i, state);
      i++;
      prob += got;
    }
    return prob;
  }

  uint8_t Order() const {
    return m_.Order();
  }

  bool RegisterWord(const StringPiece& word, const int joshua_id) {
    if (map_.size() <= joshua_id) {
      map_.resize(joshua_id + 1, 0);
    }
    bool already_present = false;
    if (map_[joshua_id] != 0)
      already_present = true;
    map_[joshua_id] = m_.GetVocabulary().Index(word);
    return already_present;
  }

private:
  Model m_;
  std::vector<lm::WordIndex> map_;
};

VirtualBase *ConstructModel(const char *file_name) {
  using namespace lm::ngram;
  ModelType model_type;
  if (!RecognizeBinary(file_name, model_type))
    model_type = HASH_PROBING;
  switch (model_type) {
  case PROBING:
    return new VirtualImpl<ProbingModel>(file_name);
  case REST_PROBING:
    return new VirtualImpl<RestProbingModel>(file_name);
  case TRIE:
    return new VirtualImpl<TrieModel>(file_name);
  case ARRAY_TRIE:
    return new VirtualImpl<ArrayTrieModel>(file_name);
  case QUANT_TRIE:
    return new VirtualImpl<QuantTrieModel>(file_name);
  case QUANT_ARRAY_TRIE:
    return new VirtualImpl<QuantArrayTrieModel>(file_name);
  default:
    UTIL_THROW(
        lm::FormatLoadException,
        "Unrecognized file format " << (unsigned) model_type
            << " in file " << file_name);
  }
}

} // namespace

extern "C" {

JNIEXPORT jlong JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_construct(
    JNIEnv *env, jclass, jstring file_name) {
  const char *str = env->GetStringUTFChars(file_name, 0);
  if (!str)
    return 0;
  jlong ret;
  try {
    ret = reinterpret_cast<jlong>(ConstructModel(str));
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
    abort();
  }
  env->ReleaseStringUTFChars(file_name, str);
  return ret;
}

JNIEXPORT void JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_destroy(
    JNIEnv *env, jclass, jlong pointer) {
  delete reinterpret_cast<VirtualBase*>(pointer);
}

JNIEXPORT jint JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_order(
    JNIEnv *env, jclass, jlong pointer) {
  return reinterpret_cast<VirtualBase*>(pointer)->Order();
}

JNIEXPORT jboolean JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_registerWord(
    JNIEnv *env, jclass, jlong pointer, jstring word, jint id) {
  const char *str = env->GetStringUTFChars(word, 0);
  if (!str)
    return false;
  jint ret;
  try {
    ret = reinterpret_cast<VirtualBase*>(pointer)->RegisterWord(str, id);
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
    abort();
  }
  env->ReleaseStringUTFChars(word, str);
  return ret;
}

JNIEXPORT jfloat JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_prob(
    JNIEnv *env, jclass, jlong pointer, jintArray arr) {
  jint length = env->GetArrayLength(arr);
  if (length <= 0)
    return 0.0;
  // GCC only.
  jint values[length];
  env->GetIntArrayRegion(arr, 0, length, values);

  return reinterpret_cast<const VirtualBase*>(pointer)->Prob(values,
      values + length);
}

JNIEXPORT jfloat JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_probString(
    JNIEnv *env, jclass, jlong pointer, jintArray arr, jint start) {
  jint length = env->GetArrayLength(arr);
  if (length <= start)
    return 0.0;
  // GCC only.
  jint values[length];
  env->GetIntArrayRegion(arr, 0, length, values);

  return reinterpret_cast<const VirtualBase*>(pointer)->ProbString(values,
      values + length, start);
}

JNIEXPORT jobject JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_probRule(
  JNIEnv *env, jclass, jlong pointer, jlongArray arr) {
  jint length = env->GetArrayLength(arr);
  // GCC only.
  jlong values[length];
  env->GetLongArrayRegion(arr, 0, length, values);

  lm::ngram::ChartState * outState = new lm::ngram::ChartState();
  float prob = reinterpret_cast<const VirtualBase*>(pointer)->ProbRule(values,
    values + length, *outState);

  // Get a class reference for the type pair we want to return (long, float).
  jclass cls = env->FindClass("joshua/decoder/ff/lm/kenlm/jni/KenLM$StateProbPair");
  if (NULL == cls) return NULL;

  // Get the Method ID of the constructor which takes an int
  jmethodID midInit = env->GetMethodID(cls, "<init>", "(JF)V");
  if (NULL == midInit) return NULL;

  // Call back constructor to allocate a new instance, with an int argument
  jobject newObj = env->NewObject(cls, midInit, (long)outState, prob);

  return newObj;
}

} // extern
<commit_msg>restored map to internal vocab<commit_after>#include "lm/enumerate_vocab.hh"
#include "lm/model.hh"
#include "lm/left.hh"
#include "lm/state.hh"
#include "util/murmur_hash.hh"

#include <iostream>

#include <string.h>
#include <stdlib.h>
#include <jni.h>
#include <pthread.h>

// Grr.  Everybody's compiler is slightly different and I'm trying to not depend on boost.
#include <ext/hash_map>

// This is needed to compile on OS X Lion / gcc 4.2.1
namespace __gnu_cxx {
template<>
struct hash<unsigned long long int> {
  size_t operator()(unsigned long long int __x) const {
    return __x;
  }
};
}

// Verify that jint and lm::ngram::WordIndex are the same size. If this breaks
// for you, there's a need to revise probString.
namespace {

template<bool> struct StaticCheck {
};

template<> struct StaticCheck<true> {
  typedef bool StaticAssertionPassed;
};

typedef StaticCheck<sizeof(jint) == sizeof(lm::WordIndex)>::StaticAssertionPassed FloatSize;

// Vocab ids above what the vocabulary knows about are unknown and should
// be mapped to that.
void MapArray(const std::vector<lm::WordIndex>& map, jint *begin, jint *end) {
  for (jint *i = begin; i < end; ++i) {
    *i = map[*i];
  }
}

char *PieceCopy(const StringPiece &str) {
  char *ret = (char*) malloc(str.size() + 1);
  memcpy(ret, str.data(), str.size());
  ret[str.size()] = 0;
  return ret;
}

// Rather than handle several different instantiations over JNI, we'll just
// do virtual calls C++-side.
class VirtualBase {
public:
  virtual ~VirtualBase() {
  }

  virtual float Prob(jint *begin, jint *end) const = 0;

  virtual float ProbRule(jlong *begin, jlong *end, lm::ngram::ChartState& state) const = 0;

  virtual float ProbString(jint * const begin, jint * const end,
      jint start) const = 0;

  virtual uint8_t Order() const = 0;

  virtual bool RegisterWord(const StringPiece& word, const int joshua_id) = 0;

protected:
  VirtualBase() {
  }

private:
};

template<class Model> class VirtualImpl: public VirtualBase {
public:
  VirtualImpl(const char *name) :
      m_(name) {
    // Insert unknown id mapping.
    map_.push_back(0);
  }

  ~VirtualImpl() {
  }

  float Prob(jint * const begin, jint * const end) const {
    MapArray(map_, begin, end);

    std::reverse(begin, end - 1);
    lm::ngram::State ignored;
    return m_.FullScoreForgotState(
        reinterpret_cast<const lm::WordIndex*>(begin),
        reinterpret_cast<const lm::WordIndex*>(end - 1), *(end - 1),
        ignored).prob;
  }

  float ProbRule(jlong * const begin, jlong * const end, lm::ngram::ChartState& state) const {
    lm::ngram::RuleScore<Model> ruleScore(m_, state);
    for (jlong* i = begin; i != end; i++) {
      long word = *i;
      if (word < 0)
        ruleScore.NonTerminal(*reinterpret_cast<const lm::ngram::ChartState*>(-word));
      else
        ruleScore.Terminal(map_[word]);
    }
    return ruleScore.Finish();
  }

  float ProbString(jint * const begin, jint * const end, jint start) const {
    MapArray(map_, begin, end);

    float prob;
    lm::ngram::State state;
    if (start == 0) {
      prob = 0;
      state = m_.NullContextState();
    } else {
      std::reverse(begin, begin + start);
      prob = m_.FullScoreForgotState(
          reinterpret_cast<const lm::WordIndex*>(begin),
          reinterpret_cast<const lm::WordIndex*>(begin + start),
          begin[start], state).prob;
      ++start;
    }
    lm::ngram::State state2;
    for (const jint *i = begin + start;;) {
      if (i >= end)
        break;
      float got = m_.Score(state, *i, state2);
      i++;
      prob += got;
      if (i >= end)
        break;
      got = m_.Score(state2, *i, state);
      i++;
      prob += got;
    }
    return prob;
  }

  uint8_t Order() const {
    return m_.Order();
  }

  bool RegisterWord(const StringPiece& word, const int joshua_id) {
    if (map_.size() <= joshua_id) {
      map_.resize(joshua_id + 1, 0);
    }
    bool already_present = false;
    if (map_[joshua_id] != 0)
      already_present = true;
    map_[joshua_id] = m_.GetVocabulary().Index(word);
    return already_present;
  }

private:
  Model m_;
  std::vector<lm::WordIndex> map_;
};

VirtualBase *ConstructModel(const char *file_name) {
  using namespace lm::ngram;
  ModelType model_type;
  if (!RecognizeBinary(file_name, model_type))
    model_type = HASH_PROBING;
  switch (model_type) {
  case PROBING:
    return new VirtualImpl<ProbingModel>(file_name);
  case REST_PROBING:
    return new VirtualImpl<RestProbingModel>(file_name);
  case TRIE:
    return new VirtualImpl<TrieModel>(file_name);
  case ARRAY_TRIE:
    return new VirtualImpl<ArrayTrieModel>(file_name);
  case QUANT_TRIE:
    return new VirtualImpl<QuantTrieModel>(file_name);
  case QUANT_ARRAY_TRIE:
    return new VirtualImpl<QuantArrayTrieModel>(file_name);
  default:
    UTIL_THROW(
        lm::FormatLoadException,
        "Unrecognized file format " << (unsigned) model_type
            << " in file " << file_name);
  }
}

} // namespace

extern "C" {

JNIEXPORT jlong JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_construct(
    JNIEnv *env, jclass, jstring file_name) {
  const char *str = env->GetStringUTFChars(file_name, 0);
  if (!str)
    return 0;
  jlong ret;
  try {
    ret = reinterpret_cast<jlong>(ConstructModel(str));
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
    abort();
  }
  env->ReleaseStringUTFChars(file_name, str);
  return ret;
}

JNIEXPORT void JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_destroy(
    JNIEnv *env, jclass, jlong pointer) {
  delete reinterpret_cast<VirtualBase*>(pointer);
}

JNIEXPORT jint JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_order(
    JNIEnv *env, jclass, jlong pointer) {
  return reinterpret_cast<VirtualBase*>(pointer)->Order();
}

JNIEXPORT jboolean JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_registerWord(
    JNIEnv *env, jclass, jlong pointer, jstring word, jint id) {
  const char *str = env->GetStringUTFChars(word, 0);
  if (!str)
    return false;
  jint ret;
  try {
    ret = reinterpret_cast<VirtualBase*>(pointer)->RegisterWord(str, id);
  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
    abort();
  }
  env->ReleaseStringUTFChars(word, str);
  return ret;
}

JNIEXPORT jfloat JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_prob(
    JNIEnv *env, jclass, jlong pointer, jintArray arr) {
  jint length = env->GetArrayLength(arr);
  if (length <= 0)
    return 0.0;
  // GCC only.
  jint values[length];
  env->GetIntArrayRegion(arr, 0, length, values);

  return reinterpret_cast<const VirtualBase*>(pointer)->Prob(values,
      values + length);
}

JNIEXPORT jfloat JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_probString(
    JNIEnv *env, jclass, jlong pointer, jintArray arr, jint start) {
  jint length = env->GetArrayLength(arr);
  if (length <= start)
    return 0.0;
  // GCC only.
  jint values[length];
  env->GetIntArrayRegion(arr, 0, length, values);

  return reinterpret_cast<const VirtualBase*>(pointer)->ProbString(values,
      values + length, start);
}

JNIEXPORT jobject JNICALL Java_joshua_decoder_ff_lm_kenlm_jni_KenLM_probRule(
  JNIEnv *env, jclass, jlong pointer, jlongArray arr) {
  jint length = env->GetArrayLength(arr);
  // GCC only.
  jlong values[length];
  env->GetLongArrayRegion(arr, 0, length, values);

  lm::ngram::ChartState * outState = new lm::ngram::ChartState();
  float prob = reinterpret_cast<const VirtualBase*>(pointer)->ProbRule(values,
    values + length, *outState);

  // Get a class reference for the type pair we want to return (long, float).
  jclass cls = env->FindClass("joshua/decoder/ff/lm/kenlm/jni/KenLM$StateProbPair");
  if (NULL == cls) return NULL;

  // Get the Method ID of the constructor which takes an int
  jmethodID midInit = env->GetMethodID(cls, "<init>", "(JF)V");
  if (NULL == midInit) return NULL;

  // Call back constructor to allocate a new instance, with an int argument
  jobject newObj = env->NewObject(cls, midInit, (long)outState, prob);

  return newObj;
}

} // extern
<|endoftext|>