/**
 * node-singlebyte
 */

// The MIT License (MIT)
//
// Copyright (c) 2013, Sergey Sokoloff (aka Mithgol the Webmaster).
// https://github.com/Mithgol/node-singlebyte
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

var extend = function(target) {
  target = target || {};
  Array.prototype.slice.call(arguments, 1).forEach(function(obj) {
    Object.keys(obj || {}).forEach(function(key) {
      target[key] = obj[key];
    });
  });
  return target;
};

var singlebyte = function(){
   /* jshint indent: false */
   if(!( this instanceof singlebyte )){
      return new singlebyte();
   }

   this.encodings = [];

   // CP437
   this.learnEncoding('cp437', this.extendASCII([
       0xC7,   0xFC,   0xE9,   0xE2,   0xE4,   0xE0,   0xE5,   0xE7,
       0xEA,   0xEB,   0xE8,   0xEF,   0xEE,   0xEC,   0xC4,   0xC5,
       0xC9,   0xE6,   0xC6,   0xF4,   0xF6,   0xF2,   0xFB,   0xF9,
       0xFF,   0xD6,   0xDC,   0xA2,   0xA3,   0xA5,  0x20A7, 0x192,
       0xE1,   0xED,   0xF3,   0xFA,   0xF1,   0xD1,   0xAA,   0xBA,
       0xBF,  0x2310,  0xAC,   0xBD,   0xBC,   0xA1,   0xAB,   0xBB,
      0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
      0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
      0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
      0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
      0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
      0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
      0x3B1,  0x3B2,  0x393,  0x3C0,  0x3A3,  0x3C3,  0x3BC,  0x3C4,
      0x3A6,  0x398,  0x3A9,  0x3B4,  0x221E, 0x3C6,  0x3B5,  0x2229,
      0x2261,  0xB1,  0x2265, 0x2264, 0x2320, 0x2321,  0xF7,  0x2248,
      0xB0,   0x2219,  0xB7,  0x221A, 0x207F,  0xB2,  0x25A0,  0xA0
   ]));

   // CP850
   this.learnEncoding('cp850', this.extendASCII([
       0xC7,   0xFC,   0xE9,   0xE2,   0xE4,   0xE0,   0xE5,   0xE7,
       0xEA,   0xEB,   0xE8,   0xEF,   0xEE,   0xEC,   0xC4,   0xC5,
       0xC9,   0xE6,   0xC6,   0xF4,   0xF6,   0xF2,   0xFB,   0xF9,
       0xFF,   0xD6,   0xDC,   0xF8,   0xA3,   0xD8,   0xD7,  0x192,
       0xE1,   0xED,   0xF3,   0xFA,   0xF1,   0xD1,   0xAA,   0xBA,
       0xBF,   0xAE,   0xAC,   0xBD,   0xBC,   0xA1,   0xAB,   0xBB,
      0x2591, 0x2592, 0x2593, 0x2502, 0x2524,  0xC1,   0xC2,   0xC0,
       0xA9,  0x2563, 0x2551, 0x2557, 0x255D,  0xA2,   0xA5,  0x2510,
      0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C,  0xE3,   0xC3,
      0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C,  0xA4,
       0xF0,   0xD0,   0xCA,   0xCB,   0xC8,  0x131,   0xCD,   0xCE,
       0xCF,  0x2518, 0x250C, 0x2588, 0x2584,  0xA6,   0xCC,  0x2580,
       0xD3,   0xDF,   0xD4,   0xD2,   0xF5,   0xD5,   0xB5,   0xFE,
       0xDE,   0xDA,   0xDB,   0xD9,   0xFD,   0xDD,   0xAF,   0xB4,
       0xAD,   0xB1,  0x2017,  0xBE,   0xB6,   0xA7,   0xF7,   0xB8,
       0xB0,   0xA8,   0xB7,   0xB9,   0xB3,   0xB2,  0x25A0,  0xA0
   ]));

   // CP858
   this.learnEncoding('cp858', this.extendASCII([
       0xC7,   0xFC,   0xE9,   0xE2,   0xE4,   0xE0,   0xE5,   0xE7,
       0xEA,   0xEB,   0xE8,   0xEF,   0xEE,   0xEC,   0xC4,   0xC5,
       0xC9,   0xE6,   0xC6,   0xF4,   0xF6,   0xF2,   0xFB,   0xF9,
       0xFF,   0xD6,   0xDC,   0xF8,   0xA3,   0xD8,   0xD7,  0x192,
       0xE1,   0xED,   0xF3,   0xFA,   0xF1,   0xD1,   0xAA,   0xBA,
       0xBF,   0xAE,   0xAC,   0xBD,   0xBC,   0xA1,   0xAB,   0xBB,
      0x2591, 0x2592, 0x2593, 0x2502, 0x2524,  0xC1,   0xC2,   0xC0,
       0xA9,  0x2563, 0x2551, 0x2557, 0x255D,  0xA2,   0xA5,  0x2510,
      0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C,  0xE3,   0xC3,
      0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C,  0xA4,
       0xF0,   0xD0,   0xCA,   0xCB,   0xC8,  0x20AC,   0xCD,   0xCE,
       0xCF,  0x2518, 0x250C, 0x2588, 0x2584,  0xA6,   0xCC,  0x2580,
       0xD3,   0xDF,   0xD4,   0xD2,   0xF5,   0xD5,   0xB5,   0xFE,
       0xDE,   0xDA,   0xDB,   0xD9,   0xFD,   0xDD,   0xAF,   0xB4,
       0xAD,   0xB1,  0x2017,  0xBE,   0xB6,   0xA7,   0xF7,   0xB8,
       0xB0,   0xA8,   0xB7,   0xB9,   0xB3,   0xB2,  0x25A0,  0xA0
   ]));

   // CP808
   this.learnEncoding('cp808', this.extendASCII([
      0x410,  0x411,  0x412,  0x413,  0x414,  0x415,  0x416,  0x417,
      0x418,  0x419,  0x41A,  0x41B,  0x41C,  0x41D,  0x41E,  0x41F,
      0x420,  0x421,  0x422,  0x423,  0x424,  0x425,  0x426,  0x427,
      0x428,  0x429,  0x42A,  0x42B,  0x42C,  0x42D,  0x42E,  0x42F,
      0x430,  0x431,  0x432,  0x433,  0x434,  0x435,  0x436,  0x437,
      0x438,  0x439,  0x43A,  0x43B,  0x43C,  0x43D,  0x43E,  0x43F,
      0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
      0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
      0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
      0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
      0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
      0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
      0x440,  0x441,  0x442,  0x443,  0x444,  0x445,  0x446,  0x447,
      0x448,  0x449,  0x44A,  0x44B,  0x44C,  0x44D,  0x44E,  0x44F,
      0x401,  0x451,  0x404,  0x454,  0x407,  0x457,  0x40E,  0x45E,
       0xB0,  0x2219,  0xB7,  0x221A, 0x2116, 0x20AC, 0x25A0,  0xA0
   ]));

   // CP866
   this.learnEncoding('cp866', this.extendASCII([
      0x410,  0x411,  0x412,  0x413,  0x414,  0x415,  0x416,  0x417,
      0x418,  0x419,  0x41A,  0x41B,  0x41C,  0x41D,  0x41E,  0x41F,
      0x420,  0x421,  0x422,  0x423,  0x424,  0x425,  0x426,  0x427,
      0x428,  0x429,  0x42A,  0x42B,  0x42C,  0x42D,  0x42E,  0x42F,
      0x430,  0x431,  0x432,  0x433,  0x434,  0x435,  0x436,  0x437,
      0x438,  0x439,  0x43A,  0x43B,  0x43C,  0x43D,  0x43E,  0x43F,
      0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
      0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
      0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
      0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
      0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
      0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
      0x440,  0x441,  0x442,  0x443,  0x444,  0x445,  0x446,  0x447,
      0x448,  0x449,  0x44A,  0x44B,  0x44C,  0x44D,  0x44E,  0x44F,
      0x401,  0x451,  0x404,  0x454,  0x407,  0x457,  0x40E,  0x45E,
       0xB0,  0x2219,  0xB7,  0x221A, 0x2116,  0xA4,  0x25A0,  0xA0
   ]));

   // CP1125
   this.learnEncoding('cp1125', this.extendASCII([
      0x410,  0x411,  0x412,  0x413,  0x414,  0x415,  0x416,  0x417,
      0x418,  0x419,  0x41A,  0x41B,  0x41C,  0x41D,  0x41E,  0x41F,
      0x420,  0x421,  0x422,  0x423,  0x424,  0x425,  0x426,  0x427,
      0x428,  0x429,  0x42A,  0x42B,  0x42C,  0x42D,  0x42E,  0x42F,
      0x430,  0x431,  0x432,  0x433,  0x434,  0x435,  0x436,  0x437,
      0x438,  0x439,  0x43A,  0x43B,  0x43C,  0x43D,  0x43E,  0x43F,
      0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
      0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
      0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
      0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
      0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
      0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
      0x440,  0x441,  0x442,  0x443,  0x444,  0x445,  0x446,  0x447,
      0x448,  0x449,  0x44A,  0x44B,  0x44C,  0x44D,  0x44E,  0x44F,
      0x401,  0x451,  0x490,  0x491,  0x404,  0x454,  0x406,  0x456,
      0x407,  0x457,   0xB7,  0x221A, 0x2116,  0xA4,  0x25A0,  0xA0
   ]));

   // KOI8-R
   this.learnEncoding('koi8-r', this.extendASCII([
      0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
      0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
      0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
      0x2264, 0x2265,  0xA0,  0x2321,  0xB0,   0xB2,   0xB7,   0xF7,
      0x2550, 0x2551, 0x2552, 0x451,  0x2553, 0x2554, 0x2555, 0x2556,
      0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E,
      0x255F, 0x2560, 0x2561, 0x401,  0x2562, 0x2563, 0x2564, 0x2565,
      0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C,  0xA9,
      0x44E,  0x430,  0x431,  0x446,  0x434,  0x435,  0x444,  0x433,
      0x445,  0x438,  0x439,  0x43A,  0x43B,  0x43C,  0x43D,  0x043E,
      0x43F,  0x44F,  0x440,  0x441,  0x442,  0x443,  0x436,  0x432,
      0x44C,  0x44B,  0x437,  0x448,  0x44D,  0x449,  0x447,  0x44A,
      0x42E,  0x410,  0x411,  0x426,  0x414,  0x415,  0x424,  0x413,
      0x425,  0x418,  0x419,  0x41A,  0x41B,  0x41C,  0x41D,  0x41E,
      0x41F,  0x42F,  0x420,  0x421,  0x422,  0x423,  0x416,  0x412,
      0x42C,  0x42B,  0x417,  0x428,  0x42D,  0x429,  0x427,  0x42A
   ]));

   // KOI8-U
   this.learnEncoding('koi8-u', this.extendASCII([
      0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
      0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
      0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
      0x2264, 0x2265,  0xA0,  0x2321,  0xB0,   0xB2,   0xB7,   0xF7,
      0x2550, 0x2551, 0x2552, 0x451,  0x454,  0x2554, 0x456,  0x457,
      0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491,  0x255D, 0x255E,
      0x255F, 0x2560, 0x2561, 0x401,  0x404,  0x2563, 0x406,  0x407,
      0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490,  0x256C,  0xA9,
      0x44E,  0x430,  0x431,  0x446,  0x434,  0x435,  0x444,  0x433,
      0x445,  0x438,  0x439,  0x43A,  0x43B,  0x43C,  0x43D,  0x043E,
      0x43F,  0x44F,  0x440,  0x441,  0x442,  0x443,  0x436,  0x432,
      0x44C,  0x44B,  0x437,  0x448,  0x44D,  0x449,  0x447,  0x44A,
      0x42E,  0x410,  0x411,  0x426,  0x414,  0x415,  0x424,  0x413,
      0x425,  0x418,  0x419,  0x41A,  0x41B,  0x41C,  0x41D,  0x41E,
      0x41F,  0x42F,  0x420,  0x421,  0x422,  0x423,  0x416,  0x412,
      0x42C,  0x42B,  0x417,  0x428,  0x42D,  0x429,  0x427,  0x42A
   ]));

   // KOI8-RU
   this.learnEncoding('koi8-ru', this.extendASCII([
      0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
      0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
      0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
      0x2264, 0x2265,  0xA0,  0x2321,  0xB0,   0xB2,   0xB7,   0xF7,
      0x2550, 0x2551, 0x2552, 0x451,  0x454,  0x2554, 0x456,  0x457,
      0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491,  0x45E,  0x255E,
      0x255F, 0x2560, 0x2561, 0x401,  0x404,  0x2563, 0x406,  0x407,
      0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490,  0x40E,   0xA9,
      0x44E,  0x430,  0x431,  0x446,  0x434,  0x435,  0x444,  0x433,
      0x445,  0x438,  0x439,  0x43A,  0x43B,  0x43C,  0x43D,  0x043E,
      0x43F,  0x44F,  0x440,  0x441,  0x442,  0x443,  0x436,  0x432,
      0x44C,  0x44B,  0x437,  0x448,  0x44D,  0x449,  0x447,  0x44A,
      0x42E,  0x410,  0x411,  0x426,  0x414,  0x415,  0x424,  0x413,
      0x425,  0x418,  0x419,  0x41A,  0x41B,  0x41C,  0x41D,  0x41E,
      0x41F,  0x42F,  0x420,  0x421,  0x422,  0x423,  0x416,  0x412,
      0x42C,  0x42B,  0x417,  0x428,  0x42D,  0x429,  0x427,  0x42A
   ]));

   // LATIN-1 aka ISO 8859-1 (Western European)
   this.learnEncoding('latin-1', this.extendASCII([
      0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
      0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
      0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
      0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
      0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
      0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
      0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
      0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
      0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
      0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
      0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
      0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
      0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
      0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
      0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
      0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
   ]));

   // Windows-1252
   this.learnEncoding('cp1252', this.extendASCII([
      0x20AC,  0x81,  0x201A, 0x192,  0x201E, 0x2026, 0x2020, 0x2021,
      0x2C6,  0x2030, 0x160,  0x2039, 0x152,   0x8D,  0x017D,  0x8F,
       0x90,  0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
      0x02DC, 0x2122, 0x161,  0x203A, 0x0153,  0x9D,  0x17E,  0x178,
       0xA0,   0xA1,   0xA2,   0xA3,   0xA4,   0xA5,   0xA6,   0xA7,
       0xA8,   0xA9,   0xAA,   0xAB,   0xAC,   0xAD,   0xAE,   0xAF,
       0xB0,   0xB1,   0xB2,   0xB3,   0xB4,   0xB5,   0xB6,   0xB7,
       0xB8,   0xB9,   0xBA,   0xBB,   0xBC,   0xBD,   0xBE,   0xBF,
       0xC0,   0xC1,   0xC2,   0xC3,   0xC4,   0xC5,   0xC6,   0xC7,
       0xC8,   0xC9,   0xCA,   0xCB,   0xCC,   0xCD,   0xCE,   0xCF,
       0xD0,   0xD1,   0xD2,   0xD3,   0xD4,   0xD5,   0xD6,   0xD7,
       0xD8,   0xD9,   0xDA,   0xDB,   0xDC,   0xDD,   0xDE,   0xDF,
       0xE0,   0xE1,   0xE2,   0xE3,   0xE4,   0xE5,   0xE6,   0xE7,
       0xE8,   0xE9,   0xEA,   0xEB,   0xEC,   0xED,   0xEE,   0xEF,
       0xF0,   0xF1,   0xF2,   0xF3,   0xF4,   0xF5,   0xF6,   0xF7,
       0xF8,   0xF9,   0xFA,   0xFB,   0xFC,   0xFD,   0xFE,   0xFF
   ]));
};

singlebyte.prototype.isEncoding = function(encodingName){
   if( Buffer.isEncoding(encodingName) ) return true;
   for( var i = 0; i < this.encodings.length; i++ ){
      if( this.encodings[i].name === encodingName ) return true;
   }
   return false;
};

singlebyte.prototype.learnEncoding = function(encodingName, encodingTable){
   /*jshint bitwise: false */
   if( Buffer.isEncoding(encodingName) ){
      throw new Error(this.errors.BUFFER_ENCODING);
   }

   if( encodingTable.length !== 256 ){
      throw new Error(this.errors.INVALID_TABLE_LENGTH);
   }

   var _this = this;
   encodingTable = encodingTable.map(function(item){
      var nextCode = item |0;
      if( 0 > nextCode || nextCode > 0x10FFFF ){
         throw new Error(_this.errors.OUT_OF_UNICODE);
      }
      return item;
   });

   if( this.isEncoding(encodingName) ){
      for( var i = 0; i < this.encodings.length; i++ ){
         if( this.encodings[i].name === encodingName ){
            this.encodings[i].table = encodingTable;
            return;
         }
      }
   } else {
      this.encodings.push({
         name:  encodingName,
         table: encodingTable
      });
   }
};

singlebyte.prototype.getEncodingTable = function(encodingName){
   for( var i = 0; i < this.encodings.length; i++ ){
      if( this.encodings[i].name === encodingName ){
         return this.encodings[i].table;
      }
   }
   return null;
};

singlebyte.prototype.extendASCII = function(extensionTable){
   if( extensionTable.length !== 128 ){
      throw new Error(this.errors.INVALID_EXTENSION);
   }

   var output = [];
   for( var i = 0; i < 128; i++ ) output.push(i);
   return output.concat(extensionTable);
};

singlebyte.prototype.bufToStr = function(buf, encoding, start, end){
   /* jshint bitwise: false */
   if(!( Buffer.isBuffer(buf) )){
      throw new Error(this.errors.NOT_A_BUFFER);
   }
   if( Buffer.isEncoding(encoding) ){
      return buf.toString(encoding, start, end);
   }
   var table = this.getEncodingTable(encoding);
   if( table === null ) throw new Error(this.errors.UNKNOWN_ENCODING);

   if( typeof end   === 'undefined' ) end   = buf.length;
   if( typeof start === 'undefined' ) start = 0;

   var output = '';
   var sourceValue;
   for( var i = start; i < end; i++ ){
      sourceValue = table[ buf[i] ];
      if( sourceValue <= 0xFFFF ){
         output += String.fromCharCode(sourceValue);
      } else if( 0x10000 <= sourceValue && sourceValue <= 0x10FFFF ){
         sourceValue -= 0x10000;
         output += String.fromCharCode( 0xD800 + (sourceValue >> 10) );
         output += String.fromCharCode( 0xDC00 + (sourceValue & 0x3FF) );
      } else throw new Error(this.errors.OUT_OF_UNICODE);
   }
   return output;
};

var strToBufDefaults = {
   defaultCode: 0x3F   // '?'
};

singlebyte.prototype.strToBuf = function(str, encoding, encodingOptions){
   if( Buffer.isEncoding(encoding) ){
      return new Buffer(str, encoding);
   }
   str = '' + str;
   var options = extend({}, strToBufDefaults, encodingOptions);
   var table = this.getEncodingTable(encoding);
   if( table === null ) throw new Error(this.errors.UNKNOWN_ENCODING);
   var output = [];
   for( var i = 0; i < str.length; i++ ){
      var charUnicode;
      var thisCharCode = str.charCodeAt(i);
      if( 0xD800 <= thisCharCode && thisCharCode <= 0xDBFF &&
         i+1 < str.length
      ){
         var nextCharCode = str.charCodeAt(i+1);
         if( 0xDC00 <= nextCharCode && nextCharCode <= 0xDFFF ){
            charUnicode = 0x10000 + (thisCharCode - 0xD800)*0x400 +
               (nextCharCode - 0xDC00);
            i++;
         } else {
            charUnicode = thisCharCode;
         }
      } else {
         charUnicode = thisCharCode;
      }

      var codeFoundIndex = table.indexOf(charUnicode);
      if( codeFoundIndex < 0 ){
         output.push(options.defaultCode);
      } else {
         output.push(codeFoundIndex);
      }
   }
   return new Buffer(output);
};

singlebyte.prototype.errors = {
   NOT_A_BUFFER : 'The given source is not a buffer!',
   UNKNOWN_ENCODING : 'The given encoding is not defined!',
   INVALID_TABLE_LENGTH : 'The encoding table must have 256 elements!',
   INVALID_EXTENSION : 'The ASCII extension table must have 128 elements!',
   BUFFER_ENCODING : "Cannot redefine a Node's encoding!",
   OUT_OF_UNICODE : "An encoding table's element is greater than 0x10FFFF!"
};

module.exports = singlebyte();
