Package: src/packages/codecs.fdoc

Codecs

key file
__init__.flx share/lib/std/codec/__init__.flx
base64.flx share/lib/std/codec/base64.flx
csv.flx share/lib/std/codec/csv.flx
uri_codec.flx share/lib/std/codec/uri_codec.flx

Synopsis

//[__init__.flx]

include "std/codec/csv";
include "std/codec/base64";
include "std/codec/uri_codec";

Base64

//[base64.flx]

//$ Base64 encode/decode functions.
//$ http://en.wikipedia.org/wiki/Base64

class Base64 {
  val b64_chars = ('A','B','C','D','E','F','G','H','I','J','K','L','M',
                   'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
                   'a','b','c','d','e','f','g','h','i','j','k','l','m',
                   'n','o','p','q','r','s','t','u','v','w','x','y','z',
                   '0','1','2','3','4','5','6','7','8','9','+','/');

  val b64_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  gen make_string: size*char->string = "::std::string ($1, $2)";

  instance Bits[char] {
    fun \& : char * char -> char = "$1&$2";
    fun \| : char * char -> char = "$1|$2";
  }

  open Bits[char];
  private fun >> : char * int -> char = "$1>>$2";
  private fun << : char * int -> char = "$1<<$2";
  private fun utiny_of: char -> utiny = "(unsigned char)$1:cast" is cast;

  // Encode function derived from encode function
  // http://www.source-code.biz/base64coder/java/Base64Coder.java.txt
  // by Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
  //$ Returns base 64 encoding of supplied string inp.
  fun encode (inp:string) => encode(inp,0,inp.len.int);

  fun encode (inp:string, iOff:int, iLen:int) : string = {
    val oDataLen = (iLen*4+2)/3;       // output length without padding
    val oLen = ((iLen+2)/3)*4;         // output length including padding
    // if using darray would use this
    //var out = darray[char]( size oLen,char(0));
    var out:string = "";//make_string(size oLen,char(0));
    var ip = iOff;
    var iEnd = iOff + iLen;
    var op = 0;
    while (ip < iEnd) do
      val i0 = inp.[ip] \& char(0xff);ip++;
      val i1 = if ip < iEnd then inp.[ip] \& char(0xff) else char(0) endif;if ip < iEnd do ip++; done
      val i2 = if ip < iEnd then inp.[ip] \& char(0xff) else char(0) endif;if ip < iEnd do ip++; done
      val o0 = i0 >> 2;
      val o1 = ((i0 \&   char(3)) << 4) \| (i1 >> 4);
      val o2 = ((i1 \& char(0xf)) << 2) \| (i2 >> 6);
      val o3 = i2 \& char(0x3F);
      out  += char (b64_chars.(utiny_of(o0)));op++;
      out  += char (b64_chars.(utiny_of(o1)));op++;
      out  += if op < oDataLen then char (b64_chars.(utiny_of(o2))) else char('=') endif;
      // if usaing darray then would use this
      //out.[op] = char (b64_chars.[utiny_of(o0)]);op++;
      //out.[op] = char (b64_chars.[utiny_of(o1)]);op++;
      //out.[op] = if op < oDataLen then char (b64_chars.[utiny_of(o2)]) else char('=') endif;
      op++;
        out += if op < oDataLen then  char(b64_chars.(utiny_of(o3))) else char('=') endif;
        //if using darray would do this
        //out.[op] = if op < oDataLen then  char(b64_chars.[utiny_of(o3)]) else char('=') endif;
      op++;
   done
   return out;
  }


  //$ Wraps encoded string after ll chars, no newline on last line.
  fun wrap (b64_str:string,ll:uint) : string = {
    var ret = "";
    val n = b64_str.len.uint;
    val whole = n/ll;
    val rmd = n%ll;
    reserve (&ret, n+whole+1u);
    for var i in 0ui upto whole - 2u do
      ret += b64_str.[i*ll to (i + 1u)*ll]+"\n";
    done
    ret += b64_str.[(whole - 1u)*ll to (whole)*ll];
    if rmd > 0u do
      ret+= "\n" + b64_str.[whole*ll to whole*ll+rmd];
    done
    return ret;
  }

  //$ Decodes supplied base 64 encoded string.
  fun decode(enc_str:string) = {
    var in_len:uint = enc_str.len.uint;
    var i:int = 0;
    var j:int = 0;
    var in_ = 0;
    var char_array_4:char^4;
    var char_array_3:char^3;
    var ret:string;

    while in_len > 0ui and ( enc_str.[in_] != char('=')) do
      //(and is_base64(enc_str[in_]))
      in_len--;
      &char_array_4.i <- enc_str.[in_]; i++; in_++;
      if (i == 4) do
        for var ip in  0 upto 3 do
          set(&char_array_4,ip, ( match find(b64_string,char_array_4.(ip)) with
            |Some v => char(v)
            |_ => char(0)
          endmatch));
        done
        set(&char_array_3,0,(char_array_4.(0) << 2) \| ((char_array_4.(1) \& char(0x30)) >> 4));
        set(&char_array_3,1,((char_array_4.(1) \& char(0xf)) << 4) \| ((char_array_4.(2) \& char(0x3c)) >> 2));
        set(&char_array_3,2,((char_array_4.(2) \& char(0x3)) << 6) \| char_array_4.(3));
        for var l in  0 upto 2 do
          ret = ret + char_array_3.(l);
        done
        i = 0;
      done
    done
  if (i > 0 ) do
    set(&char_array_4,i, char_array_3.(1));
    for var m in i upto 3 do
      set(&char_array_4,i, char(0));
    done
    for var k in 0 upto 3 do
      set(&char_array_4,k,( match find(b64_string,char_array_4.(k)) with
            |Some v => char(v)
            |_ => char(0)
          endmatch));
    done
    set(&char_array_3,0, (char_array_4.(0) << 2) \| ((char_array_4.(1) \& char(0x30)) >> 4));
    set(&char_array_3,1, ((char_array_4.(1) \& char(0xf)) << 4) \| ((char_array_4.(2) \& char(0x3c)) >> 2));
    set(&char_array_3,2, ((char_array_4.(2) \& char(0x3)) << 6) \| char_array_4.(3));

    for var n in  0 upto  (i - 2) do
       ret += char_array_3.(n);
    done
  done
  return ret;
  }

}

Csv

//[csv.flx]

//$ Comma Separated Values (CSV) reader
//$ Splits a string like 1,2,"hell" up into three strings.
class Csv {
  open List;

  //$ Fetch a value string res from position i of string s.
  //$ Update i past the comma ready to fetch another value.
  proc get_csv_value(s:string, i:&int,res:&string) {
    var r = "";
    proc add(j:int) { r += s.[j]; }
    n := s.len.int;
    enum state_t = skip,collect,quote;
    fun eq(a:state_t, b:state_t)=> caseno a == caseno b;

    var state = skip;
    ech:for var j in *i upto n - 1 do
      ch := s.[j];
      if ch == char "," do
        match state with
        | #quote => add j;
        | _ => break ech;
        endmatch;
      elif ch == char " " do
        match state with
        | #skip => continue ech;
        | #quote => add j;
        | #collect => state = skip;
        endmatch;
      elif ch == char '"' do
        match state with
        | #quote => state = skip;
        | _ => state = quote;
        endmatch;
      else
        add j;
      done;
    done;
    i <- j+1;
    res <- r;
  }

  //$ Fetch all the values in a CSV string
  //$ and return them as list.
  fun get_csv_values(s:string): list[string] = {
    var v: list[string] = Empty[string];
    var res = "";
    var pos = 0;
    n := s.len.int;
    while pos < n do
      get_csv_value (s, &pos, &res);
      if res.len.int >0 do v += res; done;
    done;
    return v;
  }
}

URI Codec

//[uri_codec.flx]

publish """
Encoder Decoders for URIs, Translates characters not allowed in URIs
to %HEX equivalants

Usage example:
open URICodec;
var s = "THis is a & test < or a url \n encoder \r\r Hello >";
var enc = uri_encode(s);
var dec = uri_decode(enc);
println("S:"+s);
println("ENC:"+enc);
println("DECX:"+dec);
"""

class URICodec {

  header """
  /* Code from http://www.zedwood.com/article/111/cpp-urlencode-function */
  std::string char2hex( char dec )
  {
    char dig1 = (dec&0xF0)>>4;
    char dig2 = (dec&0x0F);
    if ( 0<= dig1 && dig1<= 9) dig1+=48;    //0,48inascii
    if (10<= dig1 && dig1<=15) dig1+=97-10; //a,97inascii
    if ( 0<= dig2 && dig2<= 9) dig2+=48;
    if (10<= dig2 && dig2<=15) dig2+=97-10;

    std::string r;
    r.append( &dig1, 1);
    r.append( &dig2, 1);
    return r;
  }

  std::string urlencode(const std::string &c)
  {
    std::string escaped="";
    int max = c.length();
    for(int i=0; i<max; i++)
    {
      if ( (48 <= c[i] && c[i] <= 57) ||//0-9
           (65 <= c[i] && c[i] <= 90) ||//abc...xyz
           (97 <= c[i] && c[i] <= 122) || //ABC...XYZ
           (c[i]=='~' || c[i]=='!' || c[i]=='*' || c[i]=='(' || c[i]==')' || c[i]=='\\''))
        {
          escaped.append( &c[i], 1);
        }
        else
        {
          escaped.append("%");
          escaped.append( char2hex(c[i]) );//converts char 255 to string "ff"
        }
    }
    return escaped;
  }

""" requires Cxx_headers::iostream;

  gen uri_encode: string -> string = "urlencode($1)";

  private fun isxdigit_c: char -> int = "isxdigit((int)$1)" requires C89_headers::ctype_h;

  private fun isxdigit (c:char):bool => if isxdigit_c(c) == 0 then false else true endif;

  private gen strtoul: string->ulong = "strtoul ((const char *)$1.c_str(),NULL,0)";

  fun uri_decode(encoded:string):string = {
    enum decode_state { SEARCH, CONVERT };
    var state = SEARCH;
    var decoded = "";
    for var i in 0 upto (int(len(encoded)) - 1) do
      match state with
        | #SEARCH => { if encoded.[i] != char('%') do

                         decoded = decoded +
                           if encoded.[i] == char('+') then char(' ') else encoded.[i] endif;
                       else
                         state = CONVERT;
                       done
                     }
        | #CONVERT => { var temp = encoded.[i to (i+2)];
                       var both = true;
                       for var j in 0 upto 1 do
                         if not isxdigit(temp.[j]) do
                           both = false;
                         done
                       done
                       if both do
                         decoded = decoded + char(strtoul("0x"+temp));
                         i++;
                       done
                       state = SEARCH;
                      }
      endmatch;
    done
    return decoded;
  }


}