root/third_party/liblouis/nacl_wrapper/liblouis_instance.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. BytesToHexString
  2. HexStringToBytes
  3. CopyVectorToJson
  4. cc_factory_
  5. Init
  6. HandleMessage
  7. PostReply
  8. PostError
  9. PostError
  10. HandleCheckTable
  11. CheckTableInBackground
  12. HandleTranslate
  13. TranslateInBackground
  14. HandleBackTranslate
  15. BackTranslateInBackground

// Copyright 2013 Google 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 "liblouis_instance.h"

#include <cstdio>
#include <cstring>
#include <sys/mount.h>
#include <vector>

#include "nacl_io/nacl_io.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/cpp/module.h"

#include "translation_result.h"

namespace {

static const char kHexadecimalChars[] = "0123456789abcdef";

// Converts a vector of bytes into a (lowercase) hexadecimal string.
static void BytesToHexString(const std::vector<unsigned char>& bytes,
    std::string* out) {
  std::string hex;
  hex.reserve(bytes.size() * 2);
  for (size_t i = 0; i < bytes.size(); ++i) {
    unsigned char byte = bytes[i];
    hex.push_back(kHexadecimalChars[byte >> 4]);
    hex.push_back(kHexadecimalChars[byte & 0x0f]);
  }
  out->swap(hex);
}

// Converts a hexadecimal string to a vector of bytes.
// Returns false on failure.
static bool HexStringToBytes(const std::string& hex,
    std::vector<unsigned char>* out) {
  if (hex.size() % 2 != 0) {
    return false;
  }

  std::vector<unsigned char> bytes;
  bytes.reserve(hex.size() / 2);
  for (size_t i = 0; i < hex.size(); i += 2) {
    unsigned char byte;
    char ch = hex[i];
    if ('0' <= ch && ch <= '9') {
      byte = (ch - '0') << 4;
    } else if ('a' <= ch && ch <= 'f') {
      byte = (ch - 'a' + 10) << 4;
    } else if ('A' <= ch && ch <= 'F') {
      byte = (ch - 'A' + 10) << 4;
    } else {
      return false;
    }
    ch = hex[i+1];
    if ('0' <= ch && ch <= '9') {
      byte |= ch - '0';
    } else if ('a' <= ch && ch <= 'f') {
      byte |= ch - 'a' + 10;
    } else if ('A' <= ch && ch <= 'F') {
      byte |= ch - 'A' + 10;
    } else {
      return false;
    }
    bytes.push_back(byte);
  }
  out->swap(bytes);
  return true;
}

template <typename T>
static void CopyVectorToJson(const std::vector<T>& vec, Json::Value* out) {
  Json::Value result(Json::arrayValue);
  result.resize(vec.size());
  for (size_t i = 0; i < vec.size(); ++i) {
    result[i] = vec[i];
  }
  out->swap(result);
}

}  // namespace


namespace liblouis_nacl {

// Well-known strings used for configuration.
static const char kTablesDirKey[] = "tablesdir";
static const char kTablesDirDefault[] = "tables";

// Well-known strings used in JSON messages.
static const char kCommandKey[] = "command";
static const char kMessageIdKey[] = "message_id";
static const char kInReplyToKey[] = "in_reply_to";
static const char kErrorKey[] = "error";
static const char kTableNameKey[] = "table_name";
static const char kSuccessKey[] = "success";
static const char kTextKey[] = "text";
static const char kCellsKey[] = "cells";
static const char kCursorPositionKey[] = "cursor_position";
static const char kTextToBrailleKey[] = "text_to_braille";
static const char kBrailleToTextKey[] = "braille_to_text";
static const char kCheckTableCommand[] = "CheckTable";
static const char kTranslateCommand[] = "Translate";
static const char kBackTranslateCommand[] = "BackTranslate";

LibLouisInstance::LibLouisInstance(PP_Instance instance)
    : pp::Instance(instance), liblouis_thread_(this), cc_factory_(this) {}

LibLouisInstance::~LibLouisInstance() {}

bool LibLouisInstance::Init(uint32_t argc, const char* argn[],
    const char* argv[]) {
  const char* tables_dir = kTablesDirDefault;
  for (size_t i = 0; i < argc; ++i) {
    if (strcmp(argn[i], kTablesDirKey) == 0) {
      tables_dir = argv[i];
    }
  }

  nacl_io_init_ppapi(pp_instance(),
      pp::Module::Get()->get_browser_interface());
  if (mount(tables_dir, liblouis_.tables_dir(), "httpfs", 0, "") != 0) {
    // TODO(jbroman): report this error.
    return false;
  }

  return liblouis_thread_.Start();
}

void LibLouisInstance::HandleMessage(const pp::Var& var_message) {
  if (!var_message.is_string()) {
    PostError("expected message to be a JSON string");
    return;
  }

  Json::Value message;
  Json::Reader reader;
  bool parsed = reader.parse(var_message.AsString(),
      message, false /* collectComments */);
  if (!parsed) {
    PostError("expected message to be a JSON string");
    return;
  }

  Json::Value message_id = message[kMessageIdKey];
  if (!message_id.isString()) {
    PostError("expected message_id string");
    return;
  }
  std::string message_id_str = message_id.asString();

  Json::Value command = message[kCommandKey];
  if (!command.isString()) {
    PostError("expected command string", message_id_str);
    return;
  }

  std::string command_str = command.asString();
  if (command_str == kCheckTableCommand) {
    HandleCheckTable(message, message_id_str);
  } else if (command_str == kTranslateCommand) {
    HandleTranslate(message, message_id_str);
  } else if (command_str == kBackTranslateCommand) {
    HandleBackTranslate(message, message_id_str);
  } else {
    PostError("unknown command", message_id_str);
  }
}

void LibLouisInstance::PostReply(Json::Value reply,
    const std::string& in_reply_to) {
  Json::FastWriter writer;
  reply[kInReplyToKey] = in_reply_to;
  pp::Var var_reply(writer.write(reply));
  PostMessage(var_reply);
}

void LibLouisInstance::PostError(const std::string& error_message) {
  Json::FastWriter writer;
  Json::Value reply(Json::objectValue);
  reply[kErrorKey] = error_message;
  pp::Var var_reply(writer.write(reply));
  PostMessage(var_reply);
}

void LibLouisInstance::PostError(const std::string& error_message,
    const std::string& in_reply_to) {
  Json::FastWriter writer;
  Json::Value reply(Json::objectValue);
  reply[kErrorKey] = error_message;
  reply[kInReplyToKey] = in_reply_to;
  reply[kSuccessKey] = false;
  pp::Var var_reply(writer.write(reply));
  PostMessage(var_reply);
}

void LibLouisInstance::HandleCheckTable(const Json::Value& message,
    const std::string& message_id) {
  Json::Value table_name = message[kTableNameKey];
  if (!table_name.isString()) {
    PostError("expected table_name to be a string", message_id);
    return;
  }
  PostWorkToBackground(cc_factory_.NewCallback(
      &LibLouisInstance::CheckTableInBackground,
      table_name.asString(), message_id));
}

void LibLouisInstance::CheckTableInBackground(int32_t result,
    const std::string& table_name, const std::string& message_id) {
  if (result != PP_OK) {
    PostError("failed to transfer call to background thread", message_id);
    return;
  }
  bool success = liblouis_.CheckTable(table_name);
  Json::Value reply(Json::objectValue);
  reply[kSuccessKey] = success;
  PostReply(reply, message_id);
}

void LibLouisInstance::HandleTranslate(const Json::Value& message,
    const std::string& message_id) {
  Json::Value table_name = message[kTableNameKey];
  Json::Value text = message[kTextKey];
  Json::Value cursor_position = message[kCursorPositionKey];
  if (!table_name.isString()) {
    PostError("expected table_name to be a string", message_id);
    return;
  } else if (!text.isString()) {
    PostError("expected text to be a string", message_id);
    return;
  } else if (!cursor_position.isNull() && !cursor_position.isIntegral()) {
    PostError("expected cursor_position to be null or integral", message_id);
    return;
  }
  TranslationParams params;
  params.table_name = table_name.asString();
  params.text = text.asString();
  params.cursor_position = cursor_position.isIntegral() ?
      cursor_position.asInt() : -1;
  PostWorkToBackground(cc_factory_.NewCallback(
      &LibLouisInstance::TranslateInBackground,
      params, message_id));
}

void LibLouisInstance::TranslateInBackground(int32_t result,
    const TranslationParams& params, const std::string& message_id) {
  if (result != PP_OK) {
    PostError("failed to transfer call to background thread", message_id);
    return;
  }
  TranslationResult translation_result;
  bool success = liblouis_.Translate(params, &translation_result);
  Json::Value reply(Json::objectValue);
  reply[kSuccessKey] = success;
  if (success) {
    std::string hex_cells;
    Json::Value text_to_braille;
    Json::Value braille_to_text;
    BytesToHexString(translation_result.cells, &hex_cells);
    CopyVectorToJson(translation_result.text_to_braille, &text_to_braille);
    CopyVectorToJson(translation_result.braille_to_text, &braille_to_text);
    reply[kCellsKey] = hex_cells;
    reply[kTextToBrailleKey] = text_to_braille;
    reply[kBrailleToTextKey] = braille_to_text;
    if (translation_result.cursor_position >= 0) {
      reply[kCursorPositionKey] = translation_result.cursor_position;
    }
  }
  PostReply(reply, message_id);
}

void LibLouisInstance::HandleBackTranslate(const Json::Value& message,
    const std::string& message_id) {
  Json::Value table_name = message[kTableNameKey];
  Json::Value cells = message[kCellsKey];
  if (!table_name.isString()) {
    PostError("expected table_name to be a string", message_id);
    return;
  } else if (!cells.isString()) {
    PostError("expected cells to be a string", message_id);
    return;
  }
  std::vector<unsigned char> cells_vector;
  if (!HexStringToBytes(cells.asString(), &cells_vector)) {
    PostError("expected cells to be a valid hexadecimal string", message_id);
    return;
  }
  PostWorkToBackground(cc_factory_.NewCallback(
      &LibLouisInstance::BackTranslateInBackground,
      table_name.asString(), cells_vector, message_id));
}

void LibLouisInstance::BackTranslateInBackground(int32_t result,
    const std::string& table_name, const std::vector<unsigned char>& cells,
    const std::string& message_id) {
  if (result != PP_OK) {
    PostError("failed to transfer call to background thread", message_id);
    return;
  }
  std::string text;
  bool success = liblouis_.BackTranslate(table_name, cells, &text);
  Json::Value reply(Json::objectValue);
  reply[kSuccessKey] = success;
  if (success) {
    reply[kTextKey] = text;
  }
  PostReply(reply, message_id);
}

}  // namespace liblouis_nacl

/* [<][>][^][v][top][bottom][index][help] */