
/*******************************************************************/
/*                                                                 */
/*     File: utf8.C                                                */
/*   Author: Helmut Schmid                                         */
/*  Purpose:                                                       */
/*  Created: Mon Sep  5 17:49:16 2005                              */
/* Modified: Wed Sep 29 15:08:34 2010 (schmid)                     */
/*                                                                 */
/*******************************************************************/

#include "utf8.h"
#include "string.h"

namespace SFST {

const unsigned char get3LSbits = 7;
const unsigned char get4LSbits = 15;
const unsigned char get5LSbits = 31;
const unsigned char get6LSbits = 63;

const unsigned char set1MSbits = 128;
const unsigned char set2MSbits = 192;
const unsigned char set3MSbits = 224;
const unsigned char set4MSbits = 240;

/*******************************************************************/
/*                                                                 */
/*  int2utf8                                                       */
/*                                                                 */
/*******************************************************************/

char *int2utf8(unsigned int sym)

{
  static unsigned char ch[5];

  if (sym < 128) {
    // 1-byte UTF8 symbol, 7 bits
    ch[0] = (unsigned char)sym;
    ch[1] = 0;
  }

  else if (sym < 2048) {
    // 2-byte UTF8 symbol, 5+6 bits
    ch[0] = (unsigned char)((sym >> 6) | set2MSbits);
    ch[1] = (unsigned char)((sym & get6LSbits) | set1MSbits);
    ch[2] = 0;
  }

  else if (sym < 65536) {
    // 3-byte UTF8 symbol, 4+6+6 bits
    ch[0] = (unsigned char)((sym >> 12) | set3MSbits);
    ch[1] = (unsigned char)(((sym >> 6) & get6LSbits) | set1MSbits);
    ch[2] = (unsigned char)((sym & get6LSbits) | set1MSbits);
    ch[3] = 0;
  }

  else if (sym < 2097152) {
    // 4-byte UTF8 symbol, 3+6+6+6 bits
    ch[0] = (unsigned char)((sym >> 18) | set4MSbits);
    ch[1] = (unsigned char)(((sym >> 12) & get6LSbits) | set1MSbits);
    ch[2] = (unsigned char)(((sym >> 6) & get6LSbits) | set1MSbits);
    ch[3] = (unsigned char)((sym & get6LSbits) | set1MSbits);
    ch[4] = 0;
  }

  else
    return NULL;

  return (char *)ch;
}

/*******************************************************************/
/*                                                                 */
/*  utf8toint                                                      */
/*                                                                 */
/*******************************************************************/

unsigned int utf8toint(char **s)

{
  int bytes_to_come;
  unsigned int result = 0;
  unsigned char c = (unsigned char)**s;

  if (c >= (unsigned char)set4MSbits) { // 1111xxxx
    bytes_to_come = 3;
    result = (result << 3) | (c & get3LSbits);
  }

  else if (c >= (unsigned char)set3MSbits) { // 1110xxxx
    // start of a three-byte symbol
    bytes_to_come = 2;
    result = (result << 4) | (c & get4LSbits);
  }

  else if (c >= (unsigned char)set2MSbits) { // 1100xxxx
    // start of a two-byte symbol
    bytes_to_come = 1;
    result = (result << 5) | (c & get5LSbits);
  }

  else if (c < (unsigned char)set1MSbits) { // 0100xxxx
    // one-byte symbol
    bytes_to_come = 0;
    result = c;
  }

  else
    return 0; // error

  while (bytes_to_come > 0) {
    bytes_to_come--;
    (*s)++;
    c = (unsigned char)**s;
    if (c < (unsigned char)set2MSbits &&
        c >= (unsigned char)set1MSbits) // 1000xxxx
    {
      result = (result << 6) | (c & get6LSbits);
    } else
      return 0;
  }

  (*s)++;
  return result;
}

/*******************************************************************/
/*                                                                 */
/*  utf8toint                                                      */
/*                                                                 */
/*******************************************************************/

unsigned int utf8toint(char *s)

{
  unsigned int result = utf8toint(&s);
  if (*s == 0) // all bytes converted?
    return result;
  return 0;
}

} // namespace SFST
