<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A mini-zygote specifically for Native Client.

#include "chrome/common/nacl_helper_linux.h"

#include <errno.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <string>
#include <vector>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/eintr_wrapper.h"
#include "base/global_descriptors_posix.h"
#include "base/json/string_escape.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/posix/unix_domain_socket.h"
#include "base/rand_util.h"
#include "chrome/nacl/nacl_listener.h"
#include "crypto/nss_util.h"
#include "ipc/ipc_descriptors.h"
#include "ipc/ipc_switches.h"
#include "sandbox/linux/services/libc_urandom_override.h"

namespace {

// The child must mimic the behavior of zygote_main_linux.cc on the child
// side of the fork. See zygote_main_linux.cc:HandleForkRequest from
//   if (!child) {
// Note: this code doesn't attempt to support SELINUX or the SECCOMP sandbox.
void BecomeNaClLoader(const std::vector<int>& child_fds,
                      size_t prereserved_sandbox_size) {
  VLOG(1) << "NaCl loader: setting up IPC descriptor";
  // don't need zygote FD any more
  if (HANDLE_EINTR(close(kNaClZygoteDescriptor)) != 0)
    LOG(ERROR) << "close(kNaClZygoteDescriptor) failed.";
  // Set up browser descriptor on fd 3 and IPC as expected by Chrome.
  base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel,
      kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
  int zfd = dup2(child_fds[kNaClBrowserFDIndex], kNaClBrowserDescriptor);
  if (zfd != kNaClBrowserDescriptor) {
    LOG(ERROR) << "Could not initialize kNaClBrowserDescriptor";
    _exit(-1);
  }

  MessageLoopForIO main_message_loop;
  NaClListener listener;
  listener.set_prereserved_sandbox_size(prereserved_sandbox_size);
  listener.Listen();
  _exit(0);
}

// Some of this code was lifted from
// content/browser/zygote_main_linux.cc:ForkWithRealPid()
void HandleForkRequest(const std::vector<int>& child_fds,
                       size_t prereserved_sandbox_size) {
  VLOG(1) << "nacl_helper: forking";
  pid_t childpid = fork();
  if (childpid < 0) {
    perror("fork");
    LOG(ERROR) << "*** HandleForkRequest failed\n";
    // fall through to parent case below
  } else if (childpid == 0) {  // In the child process.
    bool validack = false;
    const size_t kMaxReadSize = 1024;
    char buffer[kMaxReadSize];
    // Wait until the parent process has discovered our PID.  We
    // should not fork any child processes (which the seccomp
    // sandbox does) until then, because that can interfere with the
    // parent's discovery of our PID.
    const int nread = HANDLE_EINTR(read(child_fds[kNaClParentFDIndex], buffer,
                                        kMaxReadSize));
    const std::string switch_prefix = std::string("--") +
        switches::kProcessChannelID + std::string("=");
    const size_t len = switch_prefix.length();

    if (nread < 0) {
      perror("read");
      LOG(ERROR) << "read returned " << nread;
    } else if (nread > static_cast<int>(len)) {
      if (switch_prefix.compare(0, len, buffer, 0, len) == 0) {
        VLOG(1) << "NaCl loader is synchronised with Chrome zygote";
        CommandLine::ForCurrentProcess()->AppendSwitchASCII(
            switches::kProcessChannelID,
            std::string(&buffer[len], nread - len));
        validack = true;
      }
    }
    if (HANDLE_EINTR(close(child_fds[kNaClDummyFDIndex])) != 0)
      LOG(ERROR) << "close(child_fds[kNaClDummyFDIndex]) failed";
    if (HANDLE_EINTR(close(child_fds[kNaClParentFDIndex])) != 0)
      LOG(ERROR) << "close(child_fds[kNaClParentFDIndex]) failed";
    if (validack) {
      BecomeNaClLoader(child_fds, prereserved_sandbox_size);
    } else {
      LOG(ERROR) << "Failed to synch with zygote";
    }
    // NOTREACHED
    return;
  }
  // I am the parent.
  // First, close the dummy_fd so the sandbox won't find me when
  // looking for the child's pid in /proc. Also close other fds.
  for (size_t i = 0; i < child_fds.size(); i++) {
    if (HANDLE_EINTR(close(child_fds[i])) != 0)
      LOG(ERROR) << "close(child_fds[i]) failed";
  }
  VLOG(1) << "nacl_helper: childpid is " << childpid;
  // Now tell childpid to the Chrome zygote.
  if (HANDLE_EINTR(send(kNaClZygoteDescriptor,
                        &childpid, sizeof(childpid), MSG_EOR))
      != sizeof(childpid)) {
    LOG(ERROR) << "*** send() to zygote failed";
  }
}

}  // namespace

static const char kNaClHelperReservedAtZero[] = "reserved_at_zero";
static const char kNaClHelperRDebug[] = "r_debug";

/*
 * Since we were started by nacl_helper_bootstrap rather than in the
 * usual way, the debugger cannot figure out where our executable
 * or the dynamic linker or the shared libraries are in memory,
 * so it won't find any symbols.  But we can fake it out to find us.
 *
 * The zygote passes --r_debug=0xXXXXXXXXXXXXXXXX.
 * nacl_helper_bootstrap replaces the Xs with the address of its _r_debug
 * structure.  The debugger will look for that symbol by name to
 * discover the addresses of key dynamic linker data structures.
 * Since all it knows about is the original main executable, which
 * is the bootstrap program, it finds the symbol defined there.  The
 * dynamic linker's structure is somewhere else, but it is filled in
 * after initialization.  The parts that really matter to the
 * debugger never change.  So we just copy the contents of the
 * dynamic linker's structure into the address provided by the option.
 * Hereafter, if someone attaches a debugger (or examines a core dump),
 * the debugger will find all the symbols in the normal way.
 */
static void CheckRDebug(char *argv0) {
  std::string r_debug_switch_value =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kNaClHelperRDebug);
  if (!r_debug_switch_value.empty()) {
    char *endp;
    uintptr_t r_debug_addr = strtoul(r_debug_switch_value.c_str(), &endp, 0);
    if (r_debug_addr != 0 && *endp == '\0') {
      struct r_debug *bootstrap_r_debug = (struct r_debug *) r_debug_addr;
      *bootstrap_r_debug = _r_debug;

      /*
       * Since the main executable (the bootstrap program) does not
       * have a dynamic section, the debugger will not skip the
       * first element of the link_map list as it usually would for
       * an executable or PIE that was loaded normally.  But the
       * dynamic linker has set l_name for the PIE to "" as is
       * normal for the main executable.  So the debugger doesn't
       * know which file it is.  Fill in the actual file name, which
       * came in as our argv[0].
       */
      struct link_map *l = _r_debug.r_map;
      if (l->l_name[0] == '\0')
        l->l_name = argv0;
    }
  }
}

/*
 * The zygote passes --reserved_at_zero=0xXXXXXXXXXXXXXXXX.
 * nacl_helper_bootstrap replaces the Xs with the amount of prereserved
 * sandbox memory.
 *
 * CheckReservedAtZero parses the value of the argument reserved_at_zero
 * and returns the amount of prereserved sandbox memory.
 */
static size_t CheckReservedAtZero() {
  size_t prereserved_sandbox_size = 0;
  std::string reserved_at_zero_switch_value =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
          kNaClHelperReservedAtZero);
  if (!reserved_at_zero_switch_value.empty()) {
    char *endp;
    prereserved_sandbox_size =
        strtoul(reserved_at_zero_switch_value.c_str(), &endp, 0);
    if (*endp != '\0')
      LOG(ERROR) << "Could not parse reserved_at_zero argument value of "
                 << reserved_at_zero_switch_value;
  }
  return prereserved_sandbox_size;
}

int main(int argc, char *argv[]) {
  CommandLine::Init(argc, argv);
  base::AtExitManager exit_manager;
  base::RandUint64();  // acquire /dev/urandom fd before sandbox is raised
#if !defined(CHROMIUM_SELINUX)
  // Allows NSS to fopen() /dev/urandom.
  sandbox::InitLibcUrandomOverrides();
#endif
#if defined(USE_NSS)
  // Configure NSS for use inside the NaCl process.
  // The fork check has not caused problems for NaCl, but this appears to be
  // best practice (see other places LoadNSSLibraries is called.)
  crypto::DisableNSSForkCheck();
  // Without this line on Linux, HMAC::Init will instantiate a singleton that
  // in turn attempts to open a file.  Disabling this behavior avoids a ~70 ms
  // stall the first time HMAC is used.
  crypto::ForceNSSNoDBInit();
  // Load shared libraries before sandbox is raised.
  // NSS is needed to perform hashing for validation caching.
  crypto::LoadNSSLibraries();
#endif
  std::vector<int> empty; // for SendMsg() calls
  size_t prereserved_sandbox_size = CheckReservedAtZero();

  CheckRDebug(argv[0]);

  // Send the zygote a message to let it know we are ready to help
  if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor,
                                 kNaClHelperStartupAck,
                                 sizeof(kNaClHelperStartupAck), empty)) {
    LOG(ERROR) << "*** send() to zygote failed";
  }

  while (true) {
    int badpid = -1;
    std::vector<int> fds;
    static const unsigned kMaxMessageLength = 2048;
    char buf[kMaxMessageLength];
    const ssize_t msglen = UnixDomainSocket::RecvMsg(kNaClZygoteDescriptor,
                                                     &buf, sizeof(buf), &fds);
    if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) {
      // EOF from the browser. Goodbye!
      _exit(0);
    } else if (msglen < 0) {
      LOG(ERROR) << "nacl_helper: receive from zygote failed, errno = "
                 << errno;
    } else if (msglen == sizeof(kNaClForkRequest) - 1 &&
               memcmp(buf, kNaClForkRequest, msglen) == 0) {
      if (kNaClParentFDIndex + 1 == fds.size()) {
        HandleForkRequest(fds, prereserved_sandbox_size);
        continue;  // fork succeeded. Note: child does not return
      } else {
        LOG(ERROR) << "nacl_helper: unexpected number of fds, got "
                   << fds.size();
      }
    } else {
      LOG(ERROR) << "nacl_helper unrecognized request: "
                 << base::GetDoubleQuotedJson(std::string(buf, buf + msglen));
      _exit(-1);
    }
    // if fork fails, send PID=-1 to zygote
    if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor, &badpid,
                                   sizeof(badpid), empty)) {
      LOG(ERROR) << "*** send() to zygote failed";
    }
  }
  CHECK(false);  // This routine must not return
}
<commit_msg>Redefine the ASan options for nacl_helper so that ASan does not handle SIGSEGV/SIGBUS This should allow the platform qualification test to pass and make it possible to launch NaCl programs from an instrumented browser without additional env settings. Review URL: https://chromiumcodereview.appspot.com/10830009<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A mini-zygote specifically for Native Client.

#include "chrome/common/nacl_helper_linux.h"

#include <errno.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <string>
#include <vector>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/eintr_wrapper.h"
#include "base/global_descriptors_posix.h"
#include "base/json/string_escape.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/posix/unix_domain_socket.h"
#include "base/rand_util.h"
#include "chrome/nacl/nacl_listener.h"
#include "crypto/nss_util.h"
#include "ipc/ipc_descriptors.h"
#include "ipc/ipc_switches.h"
#include "sandbox/linux/services/libc_urandom_override.h"

namespace {

// The child must mimic the behavior of zygote_main_linux.cc on the child
// side of the fork. See zygote_main_linux.cc:HandleForkRequest from
//   if (!child) {
// Note: this code doesn't attempt to support SELINUX or the SECCOMP sandbox.
void BecomeNaClLoader(const std::vector<int>& child_fds,
                      size_t prereserved_sandbox_size) {
  VLOG(1) << "NaCl loader: setting up IPC descriptor";
  // don't need zygote FD any more
  if (HANDLE_EINTR(close(kNaClZygoteDescriptor)) != 0)
    LOG(ERROR) << "close(kNaClZygoteDescriptor) failed.";
  // Set up browser descriptor on fd 3 and IPC as expected by Chrome.
  base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel,
      kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
  int zfd = dup2(child_fds[kNaClBrowserFDIndex], kNaClBrowserDescriptor);
  if (zfd != kNaClBrowserDescriptor) {
    LOG(ERROR) << "Could not initialize kNaClBrowserDescriptor";
    _exit(-1);
  }

  MessageLoopForIO main_message_loop;
  NaClListener listener;
  listener.set_prereserved_sandbox_size(prereserved_sandbox_size);
  listener.Listen();
  _exit(0);
}

// Some of this code was lifted from
// content/browser/zygote_main_linux.cc:ForkWithRealPid()
void HandleForkRequest(const std::vector<int>& child_fds,
                       size_t prereserved_sandbox_size) {
  VLOG(1) << "nacl_helper: forking";
  pid_t childpid = fork();
  if (childpid < 0) {
    perror("fork");
    LOG(ERROR) << "*** HandleForkRequest failed\n";
    // fall through to parent case below
  } else if (childpid == 0) {  // In the child process.
    bool validack = false;
    const size_t kMaxReadSize = 1024;
    char buffer[kMaxReadSize];
    // Wait until the parent process has discovered our PID.  We
    // should not fork any child processes (which the seccomp
    // sandbox does) until then, because that can interfere with the
    // parent's discovery of our PID.
    const int nread = HANDLE_EINTR(read(child_fds[kNaClParentFDIndex], buffer,
                                        kMaxReadSize));
    const std::string switch_prefix = std::string("--") +
        switches::kProcessChannelID + std::string("=");
    const size_t len = switch_prefix.length();

    if (nread < 0) {
      perror("read");
      LOG(ERROR) << "read returned " << nread;
    } else if (nread > static_cast<int>(len)) {
      if (switch_prefix.compare(0, len, buffer, 0, len) == 0) {
        VLOG(1) << "NaCl loader is synchronised with Chrome zygote";
        CommandLine::ForCurrentProcess()->AppendSwitchASCII(
            switches::kProcessChannelID,
            std::string(&buffer[len], nread - len));
        validack = true;
      }
    }
    if (HANDLE_EINTR(close(child_fds[kNaClDummyFDIndex])) != 0)
      LOG(ERROR) << "close(child_fds[kNaClDummyFDIndex]) failed";
    if (HANDLE_EINTR(close(child_fds[kNaClParentFDIndex])) != 0)
      LOG(ERROR) << "close(child_fds[kNaClParentFDIndex]) failed";
    if (validack) {
      BecomeNaClLoader(child_fds, prereserved_sandbox_size);
    } else {
      LOG(ERROR) << "Failed to synch with zygote";
    }
    // NOTREACHED
    return;
  }
  // I am the parent.
  // First, close the dummy_fd so the sandbox won't find me when
  // looking for the child's pid in /proc. Also close other fds.
  for (size_t i = 0; i < child_fds.size(); i++) {
    if (HANDLE_EINTR(close(child_fds[i])) != 0)
      LOG(ERROR) << "close(child_fds[i]) failed";
  }
  VLOG(1) << "nacl_helper: childpid is " << childpid;
  // Now tell childpid to the Chrome zygote.
  if (HANDLE_EINTR(send(kNaClZygoteDescriptor,
                        &childpid, sizeof(childpid), MSG_EOR))
      != sizeof(childpid)) {
    LOG(ERROR) << "*** send() to zygote failed";
  }
}

}  // namespace

static const char kNaClHelperReservedAtZero[] = "reserved_at_zero";
static const char kNaClHelperRDebug[] = "r_debug";

/*
 * Since we were started by nacl_helper_bootstrap rather than in the
 * usual way, the debugger cannot figure out where our executable
 * or the dynamic linker or the shared libraries are in memory,
 * so it won't find any symbols.  But we can fake it out to find us.
 *
 * The zygote passes --r_debug=0xXXXXXXXXXXXXXXXX.
 * nacl_helper_bootstrap replaces the Xs with the address of its _r_debug
 * structure.  The debugger will look for that symbol by name to
 * discover the addresses of key dynamic linker data structures.
 * Since all it knows about is the original main executable, which
 * is the bootstrap program, it finds the symbol defined there.  The
 * dynamic linker's structure is somewhere else, but it is filled in
 * after initialization.  The parts that really matter to the
 * debugger never change.  So we just copy the contents of the
 * dynamic linker's structure into the address provided by the option.
 * Hereafter, if someone attaches a debugger (or examines a core dump),
 * the debugger will find all the symbols in the normal way.
 */
static void CheckRDebug(char *argv0) {
  std::string r_debug_switch_value =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kNaClHelperRDebug);
  if (!r_debug_switch_value.empty()) {
    char *endp;
    uintptr_t r_debug_addr = strtoul(r_debug_switch_value.c_str(), &endp, 0);
    if (r_debug_addr != 0 && *endp == '\0') {
      struct r_debug *bootstrap_r_debug = (struct r_debug *) r_debug_addr;
      *bootstrap_r_debug = _r_debug;

      /*
       * Since the main executable (the bootstrap program) does not
       * have a dynamic section, the debugger will not skip the
       * first element of the link_map list as it usually would for
       * an executable or PIE that was loaded normally.  But the
       * dynamic linker has set l_name for the PIE to "" as is
       * normal for the main executable.  So the debugger doesn't
       * know which file it is.  Fill in the actual file name, which
       * came in as our argv[0].
       */
      struct link_map *l = _r_debug.r_map;
      if (l->l_name[0] == '\0')
        l->l_name = argv0;
    }
  }
}

/*
 * The zygote passes --reserved_at_zero=0xXXXXXXXXXXXXXXXX.
 * nacl_helper_bootstrap replaces the Xs with the amount of prereserved
 * sandbox memory.
 *
 * CheckReservedAtZero parses the value of the argument reserved_at_zero
 * and returns the amount of prereserved sandbox memory.
 */
static size_t CheckReservedAtZero() {
  size_t prereserved_sandbox_size = 0;
  std::string reserved_at_zero_switch_value =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
          kNaClHelperReservedAtZero);
  if (!reserved_at_zero_switch_value.empty()) {
    char *endp;
    prereserved_sandbox_size =
        strtoul(reserved_at_zero_switch_value.c_str(), &endp, 0);
    if (*endp != '\0')
      LOG(ERROR) << "Could not parse reserved_at_zero argument value of "
                 << reserved_at_zero_switch_value;
  }
  return prereserved_sandbox_size;
}

#if defined(ADDRESS_SANITIZER)
// Do not install the SIGSEGV handler in ASan. This should make the NaCl
// platform qualification test pass.
static const char kAsanDefaultOptionsNaCl[] = "handle_segv=0";

// Override the default ASan options for the NaCl helper.
// __asan_default_options should not be instrumented, because it is called
// before ASan is initialized.
extern "C"
__attribute__((no_address_safety_analysis))
const char *__asan_default_options() {
  return kAsanDefaultOptionsNaCl;
}
#endif

int main(int argc, char *argv[]) {
  CommandLine::Init(argc, argv);
  base::AtExitManager exit_manager;
  base::RandUint64();  // acquire /dev/urandom fd before sandbox is raised
#if !defined(CHROMIUM_SELINUX)
  // Allows NSS to fopen() /dev/urandom.
  sandbox::InitLibcUrandomOverrides();
#endif
#if defined(USE_NSS)
  // Configure NSS for use inside the NaCl process.
  // The fork check has not caused problems for NaCl, but this appears to be
  // best practice (see other places LoadNSSLibraries is called.)
  crypto::DisableNSSForkCheck();
  // Without this line on Linux, HMAC::Init will instantiate a singleton that
  // in turn attempts to open a file.  Disabling this behavior avoids a ~70 ms
  // stall the first time HMAC is used.
  crypto::ForceNSSNoDBInit();
  // Load shared libraries before sandbox is raised.
  // NSS is needed to perform hashing for validation caching.
  crypto::LoadNSSLibraries();
#endif
  std::vector<int> empty; // for SendMsg() calls
  size_t prereserved_sandbox_size = CheckReservedAtZero();

  CheckRDebug(argv[0]);

  // Send the zygote a message to let it know we are ready to help
  if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor,
                                 kNaClHelperStartupAck,
                                 sizeof(kNaClHelperStartupAck), empty)) {
    LOG(ERROR) << "*** send() to zygote failed";
  }

  while (true) {
    int badpid = -1;
    std::vector<int> fds;
    static const unsigned kMaxMessageLength = 2048;
    char buf[kMaxMessageLength];
    const ssize_t msglen = UnixDomainSocket::RecvMsg(kNaClZygoteDescriptor,
                                                     &buf, sizeof(buf), &fds);
    if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) {
      // EOF from the browser. Goodbye!
      _exit(0);
    } else if (msglen < 0) {
      LOG(ERROR) << "nacl_helper: receive from zygote failed, errno = "
                 << errno;
    } else if (msglen == sizeof(kNaClForkRequest) - 1 &&
               memcmp(buf, kNaClForkRequest, msglen) == 0) {
      if (kNaClParentFDIndex + 1 == fds.size()) {
        HandleForkRequest(fds, prereserved_sandbox_size);
        continue;  // fork succeeded. Note: child does not return
      } else {
        LOG(ERROR) << "nacl_helper: unexpected number of fds, got "
                   << fds.size();
      }
    } else {
      LOG(ERROR) << "nacl_helper unrecognized request: "
                 << base::GetDoubleQuotedJson(std::string(buf, buf + msglen));
      _exit(-1);
    }
    // if fork fails, send PID=-1 to zygote
    if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor, &badpid,
                                   sizeof(badpid), empty)) {
      LOG(ERROR) << "*** send() to zygote failed";
    }
  }
  CHECK(false);  // This routine must not return
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2014 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include "config.h"

#include "atomic.h"
#include "failover-table.h"
#define STATWRITER_NAMESPACE failovers
#include "statwriter.h"
#undef STATWRITER_NAMESPACE

FailoverTable::FailoverTable(size_t capacity)
    : max_entries(capacity), provider(true) {
    createEntry(0);
}

FailoverTable::FailoverTable(const std::string& json, size_t capacity)
    : max_entries(capacity), provider(true) {
    loadFromJSON(json);
    cb_assert(table.size() > 0);
}

FailoverTable::~FailoverTable() { }

failover_entry_t FailoverTable::getLatestEntry() {
    LockHolder lh(lock);
    return table.front();
}

void FailoverTable::createEntry(uint64_t high_seqno) {
    LockHolder lh(lock);
    // Our failover table represents only *our* branch of history.
    // We must remove branches we've diverged from.
    table_t::iterator itr = table.begin();
    for (; itr != table.end(); ++itr) {
        if (itr->by_seqno > high_seqno) {
            itr = table.erase(itr);
        }
    }

    if (table.empty() || table.front().by_seqno != high_seqno) {
        failover_entry_t entry;
        entry.vb_uuid = provider.next();
        entry.by_seqno = high_seqno;
        table.push_front(entry);
    }

    // Cap the size of the table
    while (table.size() > max_entries) {
        table.pop_back();
    }
}

bool FailoverTable::needsRollback(uint64_t start_seqno, uint64_t cur_seqno,
                                  uint64_t vb_uuid, uint64_t high_seqno,
                                  uint64_t* rollback_seqno) {
    LockHolder lh(lock);
    *rollback_seqno = 0;
    if (start_seqno == 0) {
        return false;
    }

    table_t::reverse_iterator itr;
    for (itr = table.rbegin(); itr != table.rend(); ++itr) {
        if (itr->vb_uuid == vb_uuid && itr->by_seqno == high_seqno) {
            uint64_t lower = itr->by_seqno;
            uint64_t upper = cur_seqno;

            ++itr;
            if (itr != table.rend()) {
                upper = itr->by_seqno;
            }

            if (start_seqno >= lower && start_seqno <= upper) {
                return false;
            }
            *rollback_seqno = upper;
            return true;
        }
    }

    return true;
}

void FailoverTable::pruneEntries(uint64_t seqno) {
    table_t::iterator it = table.begin();
    for (; it != table.end(); ++it) {
        if (it->by_seqno > seqno) {
            it = table.erase(it);
        }
    }
}

std::string FailoverTable::toJSON() {
    LockHolder lh(lock);
    cJSON* list = cJSON_CreateArray();
    table_t::iterator it;
    for(it = table.begin(); it != table.end(); it++) {
        cJSON* obj = cJSON_CreateObject();
        cJSON_AddNumberToObject(obj, "id", (*it).vb_uuid);
        cJSON_AddNumberToObject(obj, "seq", (*it).by_seqno);
        cJSON_AddItemToArray(list, obj);
    }
    char* json = cJSON_PrintUnformatted(list);
    std::string ret(json);
    free(json);
    cJSON_Delete(list);
    return ret;
}

void FailoverTable::addStats(const void* cookie, uint16_t vbid,
                             ADD_STAT add_stat) {
    LockHolder lh(lock);
    char statname[80] = {0};
    snprintf(statname, 80, "failovers:vb_%d:num_entries", vbid);
    add_casted_stat(statname, table.size(), add_stat, cookie);

    table_t::iterator it;
    int entrycounter = 0;
    for(it = table.begin(); it != table.end(); ++it) {
        snprintf(statname, 80, "failovers:vb_%d:%d:id", vbid, entrycounter);
        add_casted_stat(statname, it->vb_uuid, add_stat, cookie);
        snprintf(statname, 80, "failovers:vb_%d:%d:seq", vbid, entrycounter);
        add_casted_stat(statname, it->by_seqno, add_stat, cookie);
        entrycounter++;
    }
}

ENGINE_ERROR_CODE FailoverTable::addFailoverLog(const void* cookie,
                                                upr_add_failover_log callback) {
    LockHolder lh(lock);
    ENGINE_ERROR_CODE rv = ENGINE_SUCCESS;
    size_t logsize = table.size();

    cb_assert(logsize > 0);
    vbucket_failover_t *logentries = new vbucket_failover_t[logsize];
    vbucket_failover_t *logentry = logentries;

    table_t::iterator itr;
    for(itr = table.begin(); itr != table.end(); ++itr) {
        logentry->uuid = itr->vb_uuid;
        logentry->seqno = itr->by_seqno;
        logentry++;
    }
    rv = callback(logentries, logsize, cookie);
    delete[] logentries;

    return rv;
}

bool FailoverTable::loadFromJSON(const std::string& json) {
    cJSON* parsed = cJSON_Parse(json.c_str());

    if (parsed) {
        if (parsed->type != cJSON_Array) {
            return false;
        }

        for (cJSON* it = parsed->child; it != NULL; it = it->next) {
            if (it->type != cJSON_Object) {
                return false;
            }

            cJSON* jid = cJSON_GetObjectItem(it, "id");
            cJSON* jseq = cJSON_GetObjectItem(it, "seq");

            if (jid && jid->type != cJSON_Number) {
                return false;
            }
            if (jseq && jseq->type != cJSON_Number){
                return false;
            }

            failover_entry_t entry;
            entry.vb_uuid = (uint64_t) jid->valuedouble;
            entry.by_seqno = (uint64_t) jseq->valuedouble;
            table.push_back(entry);
        }
    }
    return true;
}

void FailoverTable::replaceFailoverLog(uint8_t* bytes, uint32_t length) {
    LockHolder lh(lock);
    cb_assert((length % 16) == 0 && length != 0);
    table.clear();

    for (; length > 0; length -=16) {
        failover_entry_t entry;
        memcpy(&entry.by_seqno, bytes + length - 8, sizeof(uint64_t));
        memcpy(&entry.vb_uuid, bytes + length - 16, sizeof(uint64_t));
        entry.by_seqno = ntohll(entry.by_seqno);
        entry.vb_uuid = ntohll(entry.vb_uuid);
        table.push_front(entry);
    }
}
<commit_msg>MB-10509: Handling vbucket uuids<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2014 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include "config.h"

#include "atomic.h"
#include "failover-table.h"
#define STATWRITER_NAMESPACE failovers
#include "statwriter.h"
#undef STATWRITER_NAMESPACE

FailoverTable::FailoverTable(size_t capacity)
    : max_entries(capacity), provider(true) {
    createEntry(0);
}

FailoverTable::FailoverTable(const std::string& json, size_t capacity)
    : max_entries(capacity), provider(true) {
    loadFromJSON(json);
    cb_assert(table.size() > 0);
}

FailoverTable::~FailoverTable() { }

failover_entry_t FailoverTable::getLatestEntry() {
    LockHolder lh(lock);
    return table.front();
}

void FailoverTable::createEntry(uint64_t high_seqno) {
    LockHolder lh(lock);
    // Our failover table represents only *our* branch of history.
    // We must remove branches we've diverged from.
    table_t::iterator itr = table.begin();
    for (; itr != table.end(); ++itr) {
        if (itr->by_seqno > high_seqno) {
            itr = table.erase(itr);
        }
    }

    if (table.empty() || table.front().by_seqno != high_seqno) {
        failover_entry_t entry;
        entry.vb_uuid = (provider.next() >> 16);
        entry.by_seqno = high_seqno;
        table.push_front(entry);
    }

    // Cap the size of the table
    while (table.size() > max_entries) {
        table.pop_back();
    }
}

bool FailoverTable::needsRollback(uint64_t start_seqno, uint64_t cur_seqno,
                                  uint64_t vb_uuid, uint64_t high_seqno,
                                  uint64_t* rollback_seqno) {
    LockHolder lh(lock);
    *rollback_seqno = 0;
    if (start_seqno == 0) {
        return false;
    }

    table_t::reverse_iterator itr;
    for (itr = table.rbegin(); itr != table.rend(); ++itr) {
        if (itr->vb_uuid == vb_uuid && itr->by_seqno == high_seqno) {
            uint64_t lower = itr->by_seqno;
            uint64_t upper = cur_seqno;

            ++itr;
            if (itr != table.rend()) {
                upper = itr->by_seqno;
            }

            if (start_seqno >= lower && start_seqno <= upper) {
                return false;
            }
            *rollback_seqno = upper;
            return true;
        }
    }

    return true;
}

void FailoverTable::pruneEntries(uint64_t seqno) {
    table_t::iterator it = table.begin();
    for (; it != table.end(); ++it) {
        if (it->by_seqno > seqno) {
            it = table.erase(it);
        }
    }
}

std::string FailoverTable::toJSON() {
    LockHolder lh(lock);
    cJSON* list = cJSON_CreateArray();
    table_t::iterator it;
    for(it = table.begin(); it != table.end(); it++) {
        cJSON* obj = cJSON_CreateObject();
        cJSON_AddNumberToObject(obj, "id", (*it).vb_uuid);
        cJSON_AddNumberToObject(obj, "seq", (*it).by_seqno);
        cJSON_AddItemToArray(list, obj);
    }
    char* json = cJSON_PrintUnformatted(list);
    std::string ret(json);
    free(json);
    cJSON_Delete(list);
    return ret;
}

void FailoverTable::addStats(const void* cookie, uint16_t vbid,
                             ADD_STAT add_stat) {
    LockHolder lh(lock);
    char statname[80] = {0};
    snprintf(statname, 80, "failovers:vb_%d:num_entries", vbid);
    add_casted_stat(statname, table.size(), add_stat, cookie);

    table_t::iterator it;
    int entrycounter = 0;
    for(it = table.begin(); it != table.end(); ++it) {
        snprintf(statname, 80, "failovers:vb_%d:%d:id", vbid, entrycounter);
        add_casted_stat(statname, it->vb_uuid, add_stat, cookie);
        snprintf(statname, 80, "failovers:vb_%d:%d:seq", vbid, entrycounter);
        add_casted_stat(statname, it->by_seqno, add_stat, cookie);
        entrycounter++;
    }
}

ENGINE_ERROR_CODE FailoverTable::addFailoverLog(const void* cookie,
                                                upr_add_failover_log callback) {
    LockHolder lh(lock);
    ENGINE_ERROR_CODE rv = ENGINE_SUCCESS;
    size_t logsize = table.size();

    cb_assert(logsize > 0);
    vbucket_failover_t *logentries = new vbucket_failover_t[logsize];
    vbucket_failover_t *logentry = logentries;

    table_t::iterator itr;
    for(itr = table.begin(); itr != table.end(); ++itr) {
        logentry->uuid = itr->vb_uuid;
        logentry->seqno = itr->by_seqno;
        logentry++;
    }
    rv = callback(logentries, logsize, cookie);
    delete[] logentries;

    return rv;
}

bool FailoverTable::loadFromJSON(const std::string& json) {
    cJSON* parsed = cJSON_Parse(json.c_str());

    if (parsed) {
        if (parsed->type != cJSON_Array) {
            return false;
        }

        for (cJSON* it = parsed->child; it != NULL; it = it->next) {
            if (it->type != cJSON_Object) {
                return false;
            }

            cJSON* jid = cJSON_GetObjectItem(it, "id");
            cJSON* jseq = cJSON_GetObjectItem(it, "seq");

            if (jid && jid->type != cJSON_Number) {
                return false;
            }
            if (jseq && jseq->type != cJSON_Number){
                return false;
            }

            failover_entry_t entry;
            entry.vb_uuid = (uint64_t) jid->valuedouble;
            entry.by_seqno = (uint64_t) jseq->valuedouble;
            table.push_back(entry);
        }
    }
    return true;
}

void FailoverTable::replaceFailoverLog(uint8_t* bytes, uint32_t length) {
    LockHolder lh(lock);
    cb_assert((length % 16) == 0 && length != 0);
    table.clear();

    for (; length > 0; length -=16) {
        failover_entry_t entry;
        memcpy(&entry.by_seqno, bytes + length - 8, sizeof(uint64_t));
        memcpy(&entry.vb_uuid, bytes + length - 16, sizeof(uint64_t));
        entry.by_seqno = ntohll(entry.by_seqno);
        entry.vb_uuid = ntohll(entry.vb_uuid);
        table.push_front(entry);
    }
}
<|endoftext|>


<commit_before>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting 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 <pdal/filters/Stats.hpp>

#include <pdal/PointBuffer.hpp>
#include <boost/algorithm/string.hpp>

namespace pdal { namespace filters {

//---------------------------------------------------------------------------
namespace stats {

boost::property_tree::ptree Collector::toPTree() const
{
    boost::property_tree::ptree tree;

    tree.put("count", count());
    tree.put("minimum", minimum());
    tree.put("maximum", maximum());
    tree.put("average", average());

    return tree;
}

} // namespace stats

//---------------------------------------------------------------------------

Stats::Stats(Stage& prevStage, const Options& options)
    : pdal::Filter(prevStage, options)
{
    return;
}

Stats::Stats(Stage& prevStage)
    : Filter(prevStage, Options::none())
{
    return;
}

Stats::~Stats()
{

}

void Stats::initialize()
{
    Filter::initialize();
    return;
}

const Options Stats::getDefaultOptions() const
{
    Options options;
    return options;
}

void Stats::processBuffer(PointBuffer& data) const
{

    return;
}

pdal::StageSequentialIterator* Stats::createSequentialIterator() const
{
    return new pdal::filters::iterators::sequential::Stats(*this);
}

namespace iterators { namespace sequential {

Stats::Stats(const pdal::filters::Stats& filter)
    : pdal::FilterSequentialIterator(filter)
    , m_statsFilter(filter)
{
    return;
}

boost::uint32_t Stats::readBufferImpl(PointBuffer& data)
{
    const boost::uint32_t numRead = getPrevIterator().read(data);

    m_statsFilter.processBuffer(data);

    const boost::uint32_t numPoints = data.getNumPoints();

    for (boost::uint32_t pointIndex=0; pointIndex < numPoints; pointIndex++)
    {
        std::multimap<DimensionPtr, stats::CollectorPtr>::const_iterator p;
        for (p = m_stats.begin(); p != m_stats.end(); ++p)
        {

            DimensionPtr d = p->first;
            stats::CollectorPtr c = p->second;

            double output = getValue(data, *d, pointIndex);
            c->insert(output);
        }
    }
    return numRead;
}

double Stats::getValue(PointBuffer& data, Dimension& d, boost::uint32_t pointIndex)
{
        double output(0.0);

    float flt(0.0);
    boost::int8_t i8(0);
    boost::uint8_t u8(0);
    boost::int16_t i16(0);
    boost::uint16_t u16(0);
    boost::int32_t i32(0);
    boost::uint32_t u32(0);
    boost::int64_t i64(0);
    boost::uint64_t u64(0);

    switch (d.getDataType())
    {
        case Dimension::Float:
            flt = data.getField<float>(d, pointIndex);
            output = static_cast<double>(flt);
            break;
        case Dimension::Double:
            output = data.getField<double>(d, pointIndex);
            break;

        case Dimension::Int8:
            i8 = data.getField<boost::int8_t>(d, pointIndex);
            output = d.applyScaling<boost::int8_t>(i8);
            break;
        case Dimension::Uint8:
            u8 = data.getField<boost::uint8_t>(d, pointIndex);
            output = d.applyScaling<boost::uint8_t>(u8);
            break;
        case Dimension::Int16:
            i16 = data.getField<boost::int16_t>(d, pointIndex);
            output = d.applyScaling<boost::int16_t>(i16);
            break;
        case Dimension::Uint16:
            u16 = data.getField<boost::uint16_t>(d, pointIndex);
            output = d.applyScaling<boost::uint16_t>(u16);
            break;
        case Dimension::Int32:
            i32 = data.getField<boost::int32_t>(d, pointIndex);
            output = d.applyScaling<boost::int32_t>(i32);
            break;
        case Dimension::Uint32:
            u32 = data.getField<boost::uint32_t>(d, pointIndex);
            output = d.applyScaling<boost::uint32_t>(u32);
            break;
        case Dimension::Int64:
            i64 = data.getField<boost::int64_t>(d, pointIndex);
            output = d.applyScaling<boost::int64_t>(i64);
            break;
        case Dimension::Uint64:
            u64 = data.getField<boost::uint64_t>(d, pointIndex);
            output = d.applyScaling<boost::uint64_t>(u64);
            break;
        case Dimension::Pointer:    // stored as 64 bits, even on a 32-bit box
        case Dimension::Undefined:
            throw pdal_error("Dimension data type unable to be summarized");
    }

    return output;
}
boost::uint64_t Stats::skipImpl(boost::uint64_t count)
{
    getPrevIterator().skip(count);
    return count;
}

bool Stats::atEndImpl() const
{
    return getPrevIterator().atEnd();
}

void Stats::readBufferBeginImpl(PointBuffer& buffer)
{
    // We'll assume you're not changing the schema per-read call
    Schema const& schema = buffer.getSchema();

    if (m_stats.size() == 0)
    {
        schema::index_by_index const& dims = schema.getDimensions().get<schema::index>();
        for (schema::index_by_index::const_iterator iter = dims.begin(); iter != dims.end(); ++iter)
        {
            DimensionPtr d = boost::shared_ptr<Dimension>(new Dimension( *iter));
            stats::CollectorPtr c = boost::shared_ptr<stats::Collector>(new stats::Collector);

            std::pair<DimensionPtr, stats::CollectorPtr> p(d,c);
            m_stats.insert(p);
        }

    }

}

boost::property_tree::ptree Stats::toPTree() const
{
    boost::property_tree::ptree tree;

    std::multimap<DimensionPtr, stats::CollectorPtr>::const_iterator p;
    for (p = m_stats.begin(); p != m_stats.end(); ++p)
    {

        const stats::CollectorPtr stat = p->second;
        boost::property_tree::ptree subtree = stat->toPTree();

        tree.add_child(p->first->getName(), subtree);

    }

    return tree;
}

stats::Collector const& Stats::getStats(Dimension const& dim) const
{
    // FIXME: do this smarter
    std::multimap<DimensionPtr, stats::CollectorPtr>::const_iterator p;
    for (p = m_stats.begin(); p != m_stats.end(); ++p)
    {
        if (dim == *p->first)
            return *p->second;
    }
    std::ostringstream oss;
    oss <<"Dimension with name '" << dim.getName() << "' not found";
    throw pdal_error(oss.str());
}

void Stats::reset()
{
    m_stats.clear();
}

} } // iterators::sequential

} } // namespaces
<commit_msg>reorganize case statement to use Interpretation/Size rather than old Dimension::Type stuff<commit_after>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting 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 <pdal/filters/Stats.hpp>

#include <pdal/PointBuffer.hpp>
#include <boost/algorithm/string.hpp>

namespace pdal { namespace filters {

//---------------------------------------------------------------------------
namespace stats {

boost::property_tree::ptree Collector::toPTree() const
{
    boost::property_tree::ptree tree;

    tree.put("count", count());
    tree.put("minimum", minimum());
    tree.put("maximum", maximum());
    tree.put("average", average());

    return tree;
}

} // namespace stats

//---------------------------------------------------------------------------

Stats::Stats(Stage& prevStage, const Options& options)
    : pdal::Filter(prevStage, options)
{
    return;
}

Stats::Stats(Stage& prevStage)
    : Filter(prevStage, Options::none())
{
    return;
}

Stats::~Stats()
{

}

void Stats::initialize()
{
    Filter::initialize();
    return;
}

const Options Stats::getDefaultOptions() const
{
    Options options;
    return options;
}

void Stats::processBuffer(PointBuffer& data) const
{

    return;
}

pdal::StageSequentialIterator* Stats::createSequentialIterator() const
{
    return new pdal::filters::iterators::sequential::Stats(*this);
}

namespace iterators { namespace sequential {

Stats::Stats(const pdal::filters::Stats& filter)
    : pdal::FilterSequentialIterator(filter)
    , m_statsFilter(filter)
{
    return;
}

boost::uint32_t Stats::readBufferImpl(PointBuffer& data)
{
    const boost::uint32_t numRead = getPrevIterator().read(data);

    m_statsFilter.processBuffer(data);

    const boost::uint32_t numPoints = data.getNumPoints();

    for (boost::uint32_t pointIndex=0; pointIndex < numPoints; pointIndex++)
    {
        std::multimap<DimensionPtr, stats::CollectorPtr>::const_iterator p;
        for (p = m_stats.begin(); p != m_stats.end(); ++p)
        {

            DimensionPtr d = p->first;
            stats::CollectorPtr c = p->second;

            double output = getValue(data, *d, pointIndex);
            c->insert(output);
        }
    }
    return numRead;
}

double Stats::getValue(PointBuffer& data, Dimension& d, boost::uint32_t pointIndex)
{
        double output(0.0);

    float flt(0.0);
    boost::int8_t i8(0);
    boost::uint8_t u8(0);
    boost::int16_t i16(0);
    boost::uint16_t u16(0);
    boost::int32_t i32(0);
    boost::uint32_t u32(0);
    boost::int64_t i64(0);
    boost::uint64_t u64(0);

    switch (d.getInterpretation())
    {
        case dimension::SignedByte:
            i8 = data.getField<boost::int8_t>(d, pointIndex);
            output = d.applyScaling<boost::int8_t>(i8);
            break;
        case dimension::UnsignedByte:
            u8 = data.getField<boost::uint8_t>(d, pointIndex);
            output = d.applyScaling<boost::uint8_t>(u8);
            break;
        case dimension::Float:
            if (d.getByteSize() == 4)
            {
                flt = data.getField<float>(d, pointIndex);
                output = static_cast<double>(flt);
                break;
            } else if (d.getByteSize() == 8)
            {
                output = data.getField<double>(d, pointIndex);
                break;
            }
            else
            {
                std::ostringstream oss;
                oss << "Unable to interpret Float of size '" << d.getByteSize() <<"'";
                throw pdal_error(oss.str());
            }

        case dimension::SignedInteger:
            if (d.getByteSize() == 1)
            {
                i8 = data.getField<boost::int8_t>(d, pointIndex);
                output = d.applyScaling<boost::int8_t>(i8);
                break;
            } else if (d.getByteSize() == 2)
            {
                i16 = data.getField<boost::int16_t>(d, pointIndex);
                output = d.applyScaling<boost::int16_t>(i16);
                break;
            } else if (d.getByteSize() == 4)
            {
                i32 = data.getField<boost::int32_t>(d, pointIndex);
                output = d.applyScaling<boost::int32_t>(i32);
                break;
            } else if (d.getByteSize() == 8)
            {
                i64 = data.getField<boost::int64_t>(d, pointIndex);
                output = d.applyScaling<boost::int64_t>(i64);
                break;
            }
            else
            {
                std::ostringstream oss;
                oss << "Unable to interpret SignedInteger of size '" << d.getByteSize() <<"'";
                throw pdal_error(oss.str());
            }
        case dimension::UnsignedInteger:
            if (d.getByteSize() == 1)
            {
                u8 = data.getField<boost::uint8_t>(d, pointIndex);
                output = d.applyScaling<boost::uint8_t>(u8);
                break;
            } else if (d.getByteSize() == 2)
            {
                u16 = data.getField<boost::uint16_t>(d, pointIndex);
                output = d.applyScaling<boost::uint16_t>(u16);
                break;
            } else if (d.getByteSize() == 4)
            {
                u32 = data.getField<boost::uint32_t>(d, pointIndex);
                output = d.applyScaling<boost::uint32_t>(u32);
                break;
            } else if (d.getByteSize() == 8)
            {
                u64 = data.getField<boost::uint64_t>(d, pointIndex);
                output = d.applyScaling<boost::uint64_t>(u64);
                break;
            }
            else
            {
                std::ostringstream oss;
                oss << "Unable to interpret UnsignedInteger of size '" << d.getByteSize() <<"'";
                throw pdal_error(oss.str());
            }

        case dimension::Pointer:    // stored as 64 bits, even on a 32-bit box
        case dimension::Undefined:
            throw pdal_error("Dimension data type unable to be summarized");
    }

    return output;
}
boost::uint64_t Stats::skipImpl(boost::uint64_t count)
{
    getPrevIterator().skip(count);
    return count;
}

bool Stats::atEndImpl() const
{
    return getPrevIterator().atEnd();
}

void Stats::readBufferBeginImpl(PointBuffer& buffer)
{
    // We'll assume you're not changing the schema per-read call
    Schema const& schema = buffer.getSchema();

    if (m_stats.size() == 0)
    {
        schema::index_by_index const& dims = schema.getDimensions().get<schema::index>();
        for (schema::index_by_index::const_iterator iter = dims.begin(); iter != dims.end(); ++iter)
        {
            DimensionPtr d = boost::shared_ptr<Dimension>(new Dimension( *iter));
            stats::CollectorPtr c = boost::shared_ptr<stats::Collector>(new stats::Collector);

            std::pair<DimensionPtr, stats::CollectorPtr> p(d,c);
            m_stats.insert(p);
        }

    }

}

boost::property_tree::ptree Stats::toPTree() const
{
    boost::property_tree::ptree tree;

    std::multimap<DimensionPtr, stats::CollectorPtr>::const_iterator p;
    for (p = m_stats.begin(); p != m_stats.end(); ++p)
    {

        const stats::CollectorPtr stat = p->second;
        boost::property_tree::ptree subtree = stat->toPTree();

        tree.add_child(p->first->getName(), subtree);

    }

    return tree;
}

stats::Collector const& Stats::getStats(Dimension const& dim) const
{
    // FIXME: do this smarter
    std::multimap<DimensionPtr, stats::CollectorPtr>::const_iterator p;
    for (p = m_stats.begin(); p != m_stats.end(); ++p)
    {
        if (dim == *p->first)
            return *p->second;
    }
    std::ostringstream oss;
    oss <<"Dimension with name '" << dim.getName() << "' not found";
    throw pdal_error(oss.str());
}

void Stats::reset()
{
    m_stats.clear();
}

} } // iterators::sequential

} } // namespaces
<|endoftext|>


<commit_before>#include "KCToolServer.h"
#include <QTcpSocket>
#include "KCClient.h"

KCToolServer::KCToolServer(KCClient *client, QObject *parent):
	QTcpServer(parent), client(client)
{
	connect(this, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}

KCToolServer::~KCToolServer()
{

}

void KCToolServer::handleRequest(QTcpSocket *socket)
{
	// Get the data out of it
	QString method = socket->property("method").toString();
	QString path = socket->property("path").toString();
	QByteArray content = socket->property("buffer").toByteArray();
	QVariantMap headers = socket->property("headers").toMap();

	// Hold response data
	int resCode(200);
	QString resMsg("OK");
	QString resType("text/json");
	QByteArray resBody = "{ \"success\": true }";

	// Handle POSTs
	if(method == "POST")
	{
		QVariant data = client->dataFromRawResponse(content);

		if(path == "/kcsapi/api_get_master/ship")
			client->_processMasterShipsData(data);
		else if(path == "/kcsapi/api_get_member/ship")
			client->_processPlayerShipsData(data);
		else if(path == "/kcsapi/api_get_member/deck")
			client->_processPlayerFleetsData(data);
		else if(path == "/kcsapi/api_get_member/ndock")
			client->_processPlayerRepairsData(data);
		else if(path == "/kcsapi/api_get_member/kdock")
			client->_processPlayerConstructionsData(data);
		else
		{
			//qDebug() << "Dunno what to do about" << path;
			resCode = 404;
			resMsg = "Not Found";
		}
	}
	// I might add other methods later (if I find a use), but for now, refuse them
	else
	{
		qDebug() << "Invalid Method:" << method << "(" << path << ")";
		resCode = 405;
		resMsg = "Method Not Allowed";
	}

	// Write out a reply to the client
	reply(socket, resCode, resMsg, resType, (resBody.size() > 0 ? resBody : resMsg.toUtf8()));

	// If it's not a Connection: close request, reset the state properties
	if(headers.value("Connection").toString() != "close")
	{
		socket->setProperty("firstLineRead", QVariant());
		socket->setProperty("headerRead", QVariant());
		socket->setProperty("method", QVariant());
		socket->setProperty("path", QVariant());
		socket->setProperty("buffer", QVariant());
		socket->setProperty("headers", QVariant());
	}
	// Otherwise, close it behind us
	else
		socket->close();
}

void KCToolServer::reply(QTcpSocket *socket, int code, QString message, QString contentType, QByteArray body)
{
	socket->write(QString("HTTP/1.1 %1 %2\r\n").arg(QString::number(code), message).toUtf8());
	socket->write(QString("Date: %1\r\n").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date)).toUtf8());
	socket->write(QString("Content-Length: %1\r\n").arg(QString::number(body.size())).toUtf8());
	socket->write(QString("Content-Type: %1\r\n").arg(contentType).toUtf8());
	socket->write(QString("\r\n").toUtf8());
	socket->write(body);
}

void KCToolServer::onNewConnection()
{
	while(this->hasPendingConnections())
	{
		QTcpSocket *socket = this->nextPendingConnection();
		connect(socket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
	}
}

void KCToolServer::onSocketReadyRead()
{
	QTcpSocket *socket = qobject_cast<QTcpSocket*>(QObject::sender());

	// Parse the first line
	if(!socket->property("firstLineRead").toBool())
	{
		QString line(socket->readLine());
		int sepPos1(line.indexOf(" "));
		int sepPos2(line.indexOf(" ", sepPos1+1));
		QString method(line.left(sepPos1));
		QString path(line.mid(sepPos1+1, sepPos2 - sepPos1 - 1));
		socket->setProperty("method", method);
		socket->setProperty("path", path);
		socket->setProperty("firstLineRead", true);
	}

	// Parse Headers!
	int contentLength = -1;
	if(!socket->property("headerRead").toBool())
	{
		QVariantMap headers(socket->property("headers").toMap());

		while(socket->canReadLine())
		{
			QString line = QString(socket->readLine()).trimmed();

			// The header section is terminated by an empty line
			if(line == "")
			{
				socket->setProperty("headerRead", true);
				break;
			}

			// Split it up
			int sepPos(line.indexOf(":"));
			QString key(line.left(sepPos).trimmed());
			QString val(line.mid(sepPos+1).trimmed());
			headers.insertMulti(key, val);

			// Check for the Content-Length header
			if(key == "Content-Length")
				contentLength = val.toInt();
		}

		socket->setProperty("headers", headers);
	}

	// Read the body into a buffer
	if(socket->bytesAvailable())
	{
		QByteArray buffer(socket->property("buffer").toByteArray());
		buffer.append(socket->readAll());
		socket->setProperty("buffer", buffer);

		// If this is a later chunk, check the old Content-Length header
		if(contentLength == -1)
			contentLength = socket->property("headers").toMap().value("Content-Length").toInt();

		// If we have a Content-Length (toLong() fails with 0)
		if(contentLength > 0 && buffer.size() >= contentLength)
		{
			this->handleRequest(socket);
			socket->close();
		}
	}
	else if(contentLength == -1 || contentLength == 0)
	{
		this->handleRequest(socket);
		socket->close();
	}
}
<commit_msg>This is how you HTTP dates<commit_after>#include "KCToolServer.h"
#include <QTcpSocket>
#include "KCClient.h"

KCToolServer::KCToolServer(KCClient *client, QObject *parent):
	QTcpServer(parent), client(client)
{
	connect(this, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}

KCToolServer::~KCToolServer()
{

}

void KCToolServer::handleRequest(QTcpSocket *socket)
{
	// Get the data out of it
	QString method = socket->property("method").toString();
	QString path = socket->property("path").toString();
	QByteArray content = socket->property("buffer").toByteArray();
	QVariantMap headers = socket->property("headers").toMap();

	// Hold response data
	int resCode(200);
	QString resMsg("OK");
	QString resType("text/json");
	QByteArray resBody = "{ \"success\": true }";

	// Handle POSTs
	if(method == "POST")
	{
		QVariant data = client->dataFromRawResponse(content);

		if(path == "/kcsapi/api_get_master/ship")
			client->_processMasterShipsData(data);
		else if(path == "/kcsapi/api_get_member/ship")
			client->_processPlayerShipsData(data);
		else if(path == "/kcsapi/api_get_member/deck")
			client->_processPlayerFleetsData(data);
		else if(path == "/kcsapi/api_get_member/ndock")
			client->_processPlayerRepairsData(data);
		else if(path == "/kcsapi/api_get_member/kdock")
			client->_processPlayerConstructionsData(data);
		else
		{
			//qDebug() << "Dunno what to do about" << path;
			resCode = 404;
			resMsg = "Not Found";
		}
	}
	// I might add other methods later (if I find a use), but for now, refuse them
	else
	{
		qDebug() << "Invalid Method:" << method << "(" << path << ")";
		resCode = 405;
		resMsg = "Method Not Allowed";
	}

	// Write out a reply to the client
	reply(socket, resCode, resMsg, resType, (resBody.size() > 0 ? resBody : resMsg.toUtf8()));

	// If it's not a Connection: close request, reset the state properties
	if(headers.value("Connection").toString() != "close")
	{
		socket->setProperty("firstLineRead", QVariant());
		socket->setProperty("headerRead", QVariant());
		socket->setProperty("method", QVariant());
		socket->setProperty("path", QVariant());
		socket->setProperty("buffer", QVariant());
		socket->setProperty("headers", QVariant());
	}
	// Otherwise, close it behind us
	else
		socket->close();
}

void KCToolServer::reply(QTcpSocket *socket, int code, QString message, QString contentType, QByteArray body)
{
	socket->write(QString("HTTP/1.1 %1 %2\r\n").arg(QString::number(code), message).toUtf8());
	socket->write(QString("Date: %1\r\n").arg(QDateTime::currentDateTimeUtc().toString("ddd, d MMM yyyy hh:mm:ss")).toUtf8());
	socket->write(QString("Content-Length: %1\r\n").arg(QString::number(body.size())).toUtf8());
	socket->write(QString("Content-Type: %1\r\n").arg(contentType).toUtf8());
	socket->write(QString("\r\n").toUtf8());
	socket->write(body);
}

void KCToolServer::onNewConnection()
{
	while(this->hasPendingConnections())
	{
		QTcpSocket *socket = this->nextPendingConnection();
		connect(socket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
	}
}

void KCToolServer::onSocketReadyRead()
{
	QTcpSocket *socket = qobject_cast<QTcpSocket*>(QObject::sender());

	// Parse the first line
	if(!socket->property("firstLineRead").toBool())
	{
		QString line(socket->readLine());
		int sepPos1(line.indexOf(" "));
		int sepPos2(line.indexOf(" ", sepPos1+1));
		QString method(line.left(sepPos1));
		QString path(line.mid(sepPos1+1, sepPos2 - sepPos1 - 1));
		socket->setProperty("method", method);
		socket->setProperty("path", path);
		socket->setProperty("firstLineRead", true);
	}

	// Parse Headers!
	int contentLength = -1;
	if(!socket->property("headerRead").toBool())
	{
		QVariantMap headers(socket->property("headers").toMap());

		while(socket->canReadLine())
		{
			QString line = QString(socket->readLine()).trimmed();

			// The header section is terminated by an empty line
			if(line == "")
			{
				socket->setProperty("headerRead", true);
				break;
			}

			// Split it up
			int sepPos(line.indexOf(":"));
			QString key(line.left(sepPos).trimmed());
			QString val(line.mid(sepPos+1).trimmed());
			headers.insertMulti(key, val);

			// Check for the Content-Length header
			if(key == "Content-Length")
				contentLength = val.toInt();
		}

		socket->setProperty("headers", headers);
	}

	// Read the body into a buffer
	if(socket->bytesAvailable())
	{
		QByteArray buffer(socket->property("buffer").toByteArray());
		buffer.append(socket->readAll());
		socket->setProperty("buffer", buffer);

		// If this is a later chunk, check the old Content-Length header
		if(contentLength == -1)
			contentLength = socket->property("headers").toMap().value("Content-Length").toInt();

		// If we have a Content-Length (toLong() fails with 0)
		if(contentLength > 0 && buffer.size() >= contentLength)
		{
			this->handleRequest(socket);
			socket->close();
		}
	}
	else if(contentLength == -1 || contentLength == 0)
	{
		this->handleRequest(socket);
		socket->close();
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "driver.h"
#include "mesh.h"
#include "texture.h"
#include "shader.h"
#include "buffer.h"
#include "framebuffer.h"

#include "common.h"

#include "../../common/color.h"
#include "../../common/log.h"

#include "../../common/debugging.h"
#ifdef GAME_DEBUGGING_ENABLED
#define CAST_PTR dynamic_cast
#else
#define CAST_PTR reinterpret_cast
#endif

namespace redc
{
  namespace gfx
  {
    namespace gl
    {
      Driver::Driver(Vec<int> size) : IDriver(size)
      {
        glViewport(0, 0, size.x, size.y);
        set_blend_policy(Blend_Policy::Transparency);

        glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

        glEnable(GL_FRAMEBUFFER_SRGB);
        glDepthFunc(GL_LESS);

        // This solves bad texture upload with non-power-of-two sized textures.
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        depth_test(true);
        write_depth(true);
      }
      Driver::~Driver()
      {
        glUseProgram(0);
      }

      std::unique_ptr<IBuffer> Driver::make_buffer_repr()
      {
        return std::make_unique<GL_Buffer>(*this);
      }
      void Driver::make_buffers(std::size_t num, std::unique_ptr<IBuffer>* buffers)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          buffers[i] = make_buffer_repr();
        }
      }
      void Driver::bind_buffer(IBuffer& buf, Buffer_Target target)
      {
        GLenum gl_target = to_gl_buffer_target(target);
        this->bind_buffer(buf, gl_target);
      }
      void Driver::bind_buffer(IBuffer& buf, GLenum target)
      {
        if(cur_buffer_ == &buf && cur_buffer_target_ == target) return;

        GL_Buffer* buffer_ptr = CAST_PTR<GL_Buffer*>(&buf);
        REDC_ASSERT_MSG(buffer_ptr, "Buffer not make with this driver;"
                        " something is very wrong.");

        buffer_ptr->bind(target);

        cur_buffer_target_ = target;
        cur_buffer_ = buffer_ptr;
      }

      std::unique_ptr<IShader> Driver::make_shader_repr()
      {
        return std::make_unique<GL_Shader>(*this);
      }
      void Driver::make_shaders(std::size_t num, std::unique_ptr<IShader>* ss)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          ss[i] = make_shader_repr();
        }
      }
      void Driver::use_shader(IShader& s, bool force)
      {
        if(&s == cur_shader_ && !force) return;

        auto shader_ptr = CAST_PTR<GL_Shader*>(&s);
        REDC_ASSERT_MSG(shader_ptr, "Shader not made with this driver;"
                        " something is very wrong.");

        shader_ptr->use();
        cur_shader_ = shader_ptr;
      }
      IShader* Driver::active_shader() const
      {
        return cur_shader_;
      }

      std::unique_ptr<IMesh> Driver::make_mesh_repr()
      {
        // Make it and forget about it, we'll just use a dynamic cast for
        // simplicity.
        return std::make_unique<GL_Mesh>(*this);
      }
      void Driver::make_meshes(std::size_t num, std::unique_ptr<IMesh>* meshes)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          meshes[i] = make_mesh_repr();
        }
      }

      void Driver::bind_mesh(IMesh& mesh, bool force)
      {
        if(&mesh == cur_mesh_ && !force) return;

        auto gl_mesh = CAST_PTR<GL_Mesh*>(&mesh);
        REDC_ASSERT_MSG(gl_mesh, "Mesh not made with this driver; something is"
                        " very wrong.");

        gl_mesh->bind();
        cur_mesh_ = gl_mesh;
      }
      void Driver::unbind_mesh()
      {
        glBindVertexArray(0);
        cur_mesh_ = nullptr;
      }

      std::unique_ptr<ITexture> Driver::make_texture_repr()
      {
        // build n' forget.
        return std::make_unique<GL_Texture>(*this);
      }
      void Driver::make_textures(std::size_t num, std::unique_ptr<ITexture>* texs)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          texs[i] = make_texture_repr();
        }
      }
      void Driver::bind_texture(ITexture& tex, Texture_Target target)
      {
        GLenum gl_target = to_gl_texture_target(target);
        bind_texture(tex, gl_target);
      }
      void Driver::bind_texture(ITexture& tex, GLenum target)
      {
        if(&tex == cur_texture_ && target == cur_texture_target_) return;

        auto gl_tex = CAST_PTR<GL_Texture*>(&tex);
        REDC_ASSERT_MSG(gl_tex, "Texture not made with this driver; something"
                        " is very wrong.");
        gl_tex->bind(target);

        cur_texture_ = &tex;
        cur_texture_target_ = target;
      }
      void Driver::active_texture(Texture_Slot loc)
      {
        glActiveTexture(GL_TEXTURE0 + loc);

        // Clear the active texture and target because they no longer apply
        cur_texture_target_ = 0;
        cur_texture_ = nullptr;
      }

      std::size_t Driver::num_texture_slots()
      {
        int units;
        glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);
        return static_cast<std::size_t>(units);
      }

      std::unique_ptr<IFramebuffer> Driver::make_framebuffer_repr()
      {
        return std::make_unique<GL_Framebuffer>(*this);
      }
      void Driver::bind_framebuffer(IFramebuffer& buf, Fbo_Binding target)
      {
        GLenum gl_target = to_gl_fbo_binding(target);
        bind_framebuffer(buf, gl_target);
      }

      GL_Framebuffer* to_gl_fbo(IFramebuffer& buf)
      {
        GL_Framebuffer* gl_fbo = CAST_PTR<GL_Framebuffer*>(&buf);
        REDC_ASSERT_MSG(gl_fbo, "Framebuffer not made with this driver;"
                        " something is very wrong.");
        return gl_fbo;
      }
      void Driver::bind_framebuffer(IFramebuffer& buf, GLenum target)
      {
        if(bound_framebuffer_ == &buf && cur_fbo_target_ == target) return;

        GL_Framebuffer* gl_fbo = to_gl_fbo(buf);
        gl_fbo->bind(target);

        bound_framebuffer_ = &buf;
        cur_fbo_target_ = target;
      }
      void Driver::use_framebuffer_draw_buffers(std::size_t num,
                                                Draw_Buffer* bufs)
      {
        // Set draw buffers
        std::vector<GLenum> draw_buffers(num);
        for(std::size_t i = 0; i < num; ++i)
        {
          draw_buffers[i] = to_gl_draw_buffer(bufs[i]);
        }

        glDrawBuffers(num, &draw_buffers[0]);

        is_default_draw_buffers_ = false;
      }
      void Driver::use_default_draw_buffers()
      {
        if(is_default_draw_buffers_ == true) return;

        // Unbind the current framebuffer. This is important, otherwise
        // GL_BACK_LEFT doesn't exist.
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        bound_framebuffer_ = nullptr;

        glDrawBuffer(GL_BACK_LEFT);

        is_default_draw_buffers_ = true;
      }

      std::unique_ptr<IRenderbuffer> Driver::make_renderbuffer_repr()
      {
        return std::make_unique<GL_Renderbuffer>(*this);
      }
      void Driver::bind_renderbuffer(IRenderbuffer& buf)
      {
        if(&buf == cur_renderbuffer_) return;

        GL_Renderbuffer* gl_rb = CAST_PTR<GL_Renderbuffer*>(&buf);
        REDC_ASSERT_MSG(gl_rb, "Renderbuffer not made with this driver;"
                        " something is very wrong.");
        gl_rb->bind();

        cur_renderbuffer_ = &buf;
      }

      void Driver::set_clear_color(Color const& c)
      {
        glClearColor(c.r / (float) 0xff, c.g / (float) 0xff,
                     c.b / (float) 0xff, c.a / (float) 0xff);
      }
      void Driver::set_clear_depth(float f)
      {
        glClearDepth(f);
      }

      void Driver::clear()
      {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      }
      void Driver::clear_color()
      {
        glClear(GL_COLOR_BUFFER_BIT);
      }
      void Driver::clear_depth()
      {
        glClear(GL_DEPTH_BUFFER_BIT);
      }
      void Driver::depth_test(bool enable)
      {
        if(enable) glEnable(GL_DEPTH_TEST);
        else glDisable(GL_DEPTH_TEST);
      }
      void Driver::write_depth(bool enable)
      {
        glDepthMask(enable);
      }
      void Driver::blending(bool enable)
      {
        if(enable) glEnable(GL_BLEND);
        else glDisable(GL_BLEND);
      }
      void Driver::face_culling(bool enable)
      {
        if(enable) glEnable(GL_CULL_FACE);
        else glDisable(GL_CULL_FACE);
      }
      void Driver::cull_side(Cull_Side side)
      {
        switch(side)
        {
        case Cull_Side::Front:
          glCullFace(GL_FRONT);
          break;
        case Cull_Side::Back:
          glCullFace(GL_BACK);
          break;
        default:
          break;
        }
      }
      void Driver::set_blend_policy(Blend_Policy policy)
      {
        switch(policy)
        {
        case Blend_Policy::Transparency:
          glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
          glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
                              GL_ZERO);
          break;
        case Blend_Policy::Additive:
          glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
          glBlendFunc(GL_ONE, GL_ONE);
          break;
        }
      }
      void Driver::check_error()
      {
        if(glGetError() == GL_INVALID_OPERATION)
        {
          log_e("OpenGL: Invalid operation");
        }
      }
    }
  }
}
<commit_msg>Use less-than-or-equal depth testing<commit_after>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "driver.h"
#include "mesh.h"
#include "texture.h"
#include "shader.h"
#include "buffer.h"
#include "framebuffer.h"

#include "common.h"

#include "../../common/color.h"
#include "../../common/log.h"

#include "../../common/debugging.h"
#ifdef GAME_DEBUGGING_ENABLED
#define CAST_PTR dynamic_cast
#else
#define CAST_PTR reinterpret_cast
#endif

namespace redc
{
  namespace gfx
  {
    namespace gl
    {
      Driver::Driver(Vec<int> size) : IDriver(size)
      {
        glViewport(0, 0, size.x, size.y);
        set_blend_policy(Blend_Policy::Transparency);

        glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

        glEnable(GL_FRAMEBUFFER_SRGB);
        glDepthFunc(GL_LEQUAL);

        // This solves bad texture upload with non-power-of-two sized textures.
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        depth_test(true);
        write_depth(true);
      }
      Driver::~Driver()
      {
        glUseProgram(0);
      }

      std::unique_ptr<IBuffer> Driver::make_buffer_repr()
      {
        return std::make_unique<GL_Buffer>(*this);
      }
      void Driver::make_buffers(std::size_t num, std::unique_ptr<IBuffer>* buffers)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          buffers[i] = make_buffer_repr();
        }
      }
      void Driver::bind_buffer(IBuffer& buf, Buffer_Target target)
      {
        GLenum gl_target = to_gl_buffer_target(target);
        this->bind_buffer(buf, gl_target);
      }
      void Driver::bind_buffer(IBuffer& buf, GLenum target)
      {
        if(cur_buffer_ == &buf && cur_buffer_target_ == target) return;

        GL_Buffer* buffer_ptr = CAST_PTR<GL_Buffer*>(&buf);
        REDC_ASSERT_MSG(buffer_ptr, "Buffer not make with this driver;"
                        " something is very wrong.");

        buffer_ptr->bind(target);

        cur_buffer_target_ = target;
        cur_buffer_ = buffer_ptr;
      }

      std::unique_ptr<IShader> Driver::make_shader_repr()
      {
        return std::make_unique<GL_Shader>(*this);
      }
      void Driver::make_shaders(std::size_t num, std::unique_ptr<IShader>* ss)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          ss[i] = make_shader_repr();
        }
      }
      void Driver::use_shader(IShader& s, bool force)
      {
        if(&s == cur_shader_ && !force) return;

        auto shader_ptr = CAST_PTR<GL_Shader*>(&s);
        REDC_ASSERT_MSG(shader_ptr, "Shader not made with this driver;"
                        " something is very wrong.");

        shader_ptr->use();
        cur_shader_ = shader_ptr;
      }
      IShader* Driver::active_shader() const
      {
        return cur_shader_;
      }

      std::unique_ptr<IMesh> Driver::make_mesh_repr()
      {
        // Make it and forget about it, we'll just use a dynamic cast for
        // simplicity.
        return std::make_unique<GL_Mesh>(*this);
      }
      void Driver::make_meshes(std::size_t num, std::unique_ptr<IMesh>* meshes)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          meshes[i] = make_mesh_repr();
        }
      }

      void Driver::bind_mesh(IMesh& mesh, bool force)
      {
        if(&mesh == cur_mesh_ && !force) return;

        auto gl_mesh = CAST_PTR<GL_Mesh*>(&mesh);
        REDC_ASSERT_MSG(gl_mesh, "Mesh not made with this driver; something is"
                        " very wrong.");

        gl_mesh->bind();
        cur_mesh_ = gl_mesh;
      }
      void Driver::unbind_mesh()
      {
        glBindVertexArray(0);
        cur_mesh_ = nullptr;
      }

      std::unique_ptr<ITexture> Driver::make_texture_repr()
      {
        // build n' forget.
        return std::make_unique<GL_Texture>(*this);
      }
      void Driver::make_textures(std::size_t num, std::unique_ptr<ITexture>* texs)
      {
        for(std::size_t i = 0; i < num; ++i)
        {
          texs[i] = make_texture_repr();
        }
      }
      void Driver::bind_texture(ITexture& tex, Texture_Target target)
      {
        GLenum gl_target = to_gl_texture_target(target);
        bind_texture(tex, gl_target);
      }
      void Driver::bind_texture(ITexture& tex, GLenum target)
      {
        if(&tex == cur_texture_ && target == cur_texture_target_) return;

        auto gl_tex = CAST_PTR<GL_Texture*>(&tex);
        REDC_ASSERT_MSG(gl_tex, "Texture not made with this driver; something"
                        " is very wrong.");
        gl_tex->bind(target);

        cur_texture_ = &tex;
        cur_texture_target_ = target;
      }
      void Driver::active_texture(Texture_Slot loc)
      {
        glActiveTexture(GL_TEXTURE0 + loc);

        // Clear the active texture and target because they no longer apply
        cur_texture_target_ = 0;
        cur_texture_ = nullptr;
      }

      std::size_t Driver::num_texture_slots()
      {
        int units;
        glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);
        return static_cast<std::size_t>(units);
      }

      std::unique_ptr<IFramebuffer> Driver::make_framebuffer_repr()
      {
        return std::make_unique<GL_Framebuffer>(*this);
      }
      void Driver::bind_framebuffer(IFramebuffer& buf, Fbo_Binding target)
      {
        GLenum gl_target = to_gl_fbo_binding(target);
        bind_framebuffer(buf, gl_target);
      }

      GL_Framebuffer* to_gl_fbo(IFramebuffer& buf)
      {
        GL_Framebuffer* gl_fbo = CAST_PTR<GL_Framebuffer*>(&buf);
        REDC_ASSERT_MSG(gl_fbo, "Framebuffer not made with this driver;"
                        " something is very wrong.");
        return gl_fbo;
      }
      void Driver::bind_framebuffer(IFramebuffer& buf, GLenum target)
      {
        if(bound_framebuffer_ == &buf && cur_fbo_target_ == target) return;

        GL_Framebuffer* gl_fbo = to_gl_fbo(buf);
        gl_fbo->bind(target);

        bound_framebuffer_ = &buf;
        cur_fbo_target_ = target;
      }
      void Driver::use_framebuffer_draw_buffers(std::size_t num,
                                                Draw_Buffer* bufs)
      {
        // Set draw buffers
        std::vector<GLenum> draw_buffers(num);
        for(std::size_t i = 0; i < num; ++i)
        {
          draw_buffers[i] = to_gl_draw_buffer(bufs[i]);
        }

        glDrawBuffers(num, &draw_buffers[0]);

        is_default_draw_buffers_ = false;
      }
      void Driver::use_default_draw_buffers()
      {
        if(is_default_draw_buffers_ == true) return;

        // Unbind the current framebuffer. This is important, otherwise
        // GL_BACK_LEFT doesn't exist.
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        bound_framebuffer_ = nullptr;

        glDrawBuffer(GL_BACK_LEFT);

        is_default_draw_buffers_ = true;
      }

      std::unique_ptr<IRenderbuffer> Driver::make_renderbuffer_repr()
      {
        return std::make_unique<GL_Renderbuffer>(*this);
      }
      void Driver::bind_renderbuffer(IRenderbuffer& buf)
      {
        if(&buf == cur_renderbuffer_) return;

        GL_Renderbuffer* gl_rb = CAST_PTR<GL_Renderbuffer*>(&buf);
        REDC_ASSERT_MSG(gl_rb, "Renderbuffer not made with this driver;"
                        " something is very wrong.");
        gl_rb->bind();

        cur_renderbuffer_ = &buf;
      }

      void Driver::set_clear_color(Color const& c)
      {
        glClearColor(c.r / (float) 0xff, c.g / (float) 0xff,
                     c.b / (float) 0xff, c.a / (float) 0xff);
      }
      void Driver::set_clear_depth(float f)
      {
        glClearDepth(f);
      }

      void Driver::clear()
      {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      }
      void Driver::clear_color()
      {
        glClear(GL_COLOR_BUFFER_BIT);
      }
      void Driver::clear_depth()
      {
        glClear(GL_DEPTH_BUFFER_BIT);
      }
      void Driver::depth_test(bool enable)
      {
        if(enable) glEnable(GL_DEPTH_TEST);
        else glDisable(GL_DEPTH_TEST);
      }
      void Driver::write_depth(bool enable)
      {
        glDepthMask(enable);
      }
      void Driver::blending(bool enable)
      {
        if(enable) glEnable(GL_BLEND);
        else glDisable(GL_BLEND);
      }
      void Driver::face_culling(bool enable)
      {
        if(enable) glEnable(GL_CULL_FACE);
        else glDisable(GL_CULL_FACE);
      }
      void Driver::cull_side(Cull_Side side)
      {
        switch(side)
        {
        case Cull_Side::Front:
          glCullFace(GL_FRONT);
          break;
        case Cull_Side::Back:
          glCullFace(GL_BACK);
          break;
        default:
          break;
        }
      }
      void Driver::set_blend_policy(Blend_Policy policy)
      {
        switch(policy)
        {
        case Blend_Policy::Transparency:
          glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
          glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
                              GL_ZERO);
          break;
        case Blend_Policy::Additive:
          glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
          glBlendFunc(GL_ONE, GL_ONE);
          break;
        }
      }
      void Driver::check_error()
      {
        if(glGetError() == GL_INVALID_OPERATION)
        {
          log_e("OpenGL: Invalid operation");
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: breakiterator_th.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: rt $ $Date: 2005-10-17 15:42:14 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#include <breakiterator_th.hxx>
#include <wtt.h>

#include <string.h> // for memset

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::rtl;

namespace com { namespace sun { namespace star { namespace i18n {

//  ----------------------------------------------------
//  class Breakiterator_th
//  ----------------------------------------------------;
BreakIterator_th::BreakIterator_th()
{
    cBreakIterator = "com.sun.star.i18n.BreakIterator_th";
    wordRule=NULL;
}

BreakIterator_th::~BreakIterator_th()
{
}

#define SARA_AM 0x0E33

/*
 * cell composition states
 */

#define ST_COM  1   // Compose the following character with leading char and display in the same cell
#define ST_NXT  2   // display the following character in the next cell
#define ST_NDP  3   // non-display

static const sal_Int16 thaiCompRel[MAX_CT][MAX_CT] = {
    //  C  N  C  L  F  F  F  B  B  B  T  A  A  A  A  A  A
    //  T  O  O  V  V  V  V  V  V  D  O  D  D  D  V  V  V
    //  R  N  N     1  2  3  1  2     N  1  2  3  1  2  3
    //  L     S                       E
    //  0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // CTRL 0
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // NON  1
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM   }, // CONS 2
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // LV   3
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // FV1  4
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // FV2  5
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // FV3  6
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // BV1  7
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // BV2  8
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // BD   9
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // TONE 10
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AD1  11
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AD2  12
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AD3  13
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AV1  14
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AV2  15
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_NXT, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT   } // AV3  16

};

const sal_uInt32 is_ST_COM = (1<<CT_CTRL)|(1<<CT_NON)|(1<<CT_CONS)|(1<<CT_TONE);

static sal_uInt16 SAL_CALL getCombState(const sal_Unicode *text, sal_Int32 pos)
{
    sal_uInt16 ch1 = getCharType(text[pos]);
    sal_uInt16 ch2 = getCharType(text[pos+1]);

    if (text[pos+1] == SARA_AM) {
        if ((1 << ch1) & is_ST_COM)
        return  ST_COM;
        else
        ch2 = CT_AD1;
    }

    return thaiCompRel[ch1][ch2];
}

static sal_Int32 SAL_CALL getACell(const sal_Unicode *text, sal_Int32 pos, sal_Int32 len)
{
    sal_uInt32 curr = 1;
    for (; pos + 1 < len && getCombState(text, pos) == ST_COM; curr++, pos++) {}
    return curr;
}

#define is_Thai(c)  (0x0e00 <= c && c <= 0x0e7f) // Unicode definition for Thai

void SAL_CALL BreakIterator_th::makeIndex(const OUString& Text, sal_Int32 nStartPos)
    throw(RuntimeException)
{
    if (Text != cachedText) {
        cachedText = Text;
        if (cellIndexSize < cachedText.getLength()) {
        cellIndexSize = cachedText.getLength();
        free(nextCellIndex);
        free(previousCellIndex);
        nextCellIndex = (sal_Int32*) calloc(cellIndexSize, sizeof(sal_Int32));
        previousCellIndex = (sal_Int32*) calloc(cellIndexSize, sizeof(sal_Int32));
        }
        // reset nextCell for new Text
        memset(nextCellIndex, 0, cellIndexSize * sizeof(sal_Int32));
    }
    else if (nextCellIndex[nStartPos] > 0 || ! is_Thai(Text[nStartPos]))
        return;

    const sal_Unicode* str = cachedText.getStr();
    sal_Int32 len = cachedText.getLength(), startPos, endPos;

    startPos = nStartPos;
    while (startPos > 0 && is_Thai(str[startPos-1])) startPos--;
    endPos = nStartPos+1;
    while (endPos < len && is_Thai(str[endPos])) endPos++;

    sal_Int32 start, end, pos;
    pos = start = end = startPos;

    while (pos < endPos) {
        end += getACell(str, start, endPos);
        while (pos < end) {
        nextCellIndex[pos] = end;
        previousCellIndex[pos] = start;
        pos++;
        }
        start = end;
    }
}

} } } }
<commit_msg>INTEGRATION: CWS thaiissues (1.8.6); FILE MERGED 2005/10/26 20:42:39 khong 1.8.6.1: use icu thai linke break algorithm for thai breakiterator<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: breakiterator_th.cxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: obo $ $Date: 2005-11-16 10:18:25 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#include <breakiterator_th.hxx>
#include <wtt.h>

#include <string.h> // for memset

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::rtl;

namespace com { namespace sun { namespace star { namespace i18n {

//  ----------------------------------------------------
//  class Breakiterator_th
//  ----------------------------------------------------;
BreakIterator_th::BreakIterator_th()
{
    cBreakIterator = "com.sun.star.i18n.BreakIterator_th";
    wordRule=lineRule=NULL;
}

BreakIterator_th::~BreakIterator_th()
{
}

#define SARA_AM 0x0E33

/*
 * cell composition states
 */

#define ST_COM  1   // Compose the following character with leading char and display in the same cell
#define ST_NXT  2   // display the following character in the next cell
#define ST_NDP  3   // non-display

static const sal_Int16 thaiCompRel[MAX_CT][MAX_CT] = {
    //  C  N  C  L  F  F  F  B  B  B  T  A  A  A  A  A  A
    //  T  O  O  V  V  V  V  V  V  D  O  D  D  D  V  V  V
    //  R  N  N     1  2  3  1  2     N  1  2  3  1  2  3
    //  L     S                       E
    //  0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // CTRL 0
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // NON  1
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM, ST_COM   }, // CONS 2
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // LV   3
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // FV1  4
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // FV2  5
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // FV3  6
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // BV1  7
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // BV2  8
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // BD   9
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // TONE 10
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AD1  11
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AD2  12
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AD3  13
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AV1  14
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT   }, // AV2  15
    {   ST_NDP, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_NXT, ST_COM, ST_NXT, ST_COM, ST_NXT, ST_NXT, ST_NXT, ST_NXT   } // AV3  16

};

const sal_uInt32 is_ST_COM = (1<<CT_CTRL)|(1<<CT_NON)|(1<<CT_CONS)|(1<<CT_TONE);

static sal_uInt16 SAL_CALL getCombState(const sal_Unicode *text, sal_Int32 pos)
{
    sal_uInt16 ch1 = getCharType(text[pos]);
    sal_uInt16 ch2 = getCharType(text[pos+1]);

    if (text[pos+1] == SARA_AM) {
        if ((1 << ch1) & is_ST_COM)
        return  ST_COM;
        else
        ch2 = CT_AD1;
    }

    return thaiCompRel[ch1][ch2];
}

static sal_Int32 SAL_CALL getACell(const sal_Unicode *text, sal_Int32 pos, sal_Int32 len)
{
    sal_uInt32 curr = 1;
    for (; pos + 1 < len && getCombState(text, pos) == ST_COM; curr++, pos++) {}
    return curr;
}

#define is_Thai(c)  (0x0e00 <= c && c <= 0x0e7f) // Unicode definition for Thai

void SAL_CALL BreakIterator_th::makeIndex(const OUString& Text, sal_Int32 nStartPos)
    throw(RuntimeException)
{
    if (Text != cachedText) {
        cachedText = Text;
        if (cellIndexSize < cachedText.getLength()) {
        cellIndexSize = cachedText.getLength();
        free(nextCellIndex);
        free(previousCellIndex);
        nextCellIndex = (sal_Int32*) calloc(cellIndexSize, sizeof(sal_Int32));
        previousCellIndex = (sal_Int32*) calloc(cellIndexSize, sizeof(sal_Int32));
        }
        // reset nextCell for new Text
        memset(nextCellIndex, 0, cellIndexSize * sizeof(sal_Int32));
    }
    else if (nextCellIndex[nStartPos] > 0 || ! is_Thai(Text[nStartPos]))
        return;

    const sal_Unicode* str = cachedText.getStr();
    sal_Int32 len = cachedText.getLength(), startPos, endPos;

    startPos = nStartPos;
    while (startPos > 0 && is_Thai(str[startPos-1])) startPos--;
    endPos = nStartPos+1;
    while (endPos < len && is_Thai(str[endPos])) endPos++;

    sal_Int32 start, end, pos;
    pos = start = end = startPos;

    while (pos < endPos) {
        end += getACell(str, start, endPos);
        while (pos < end) {
        nextCellIndex[pos] = end;
        previousCellIndex[pos] = start;
        pos++;
        }
        start = end;
    }
}

} } } }
<|endoftext|>