]> Dogcows Code - chaz/yoink/blobdiff - src/stlplus/strings/string_inf.cpp
testing new non-autotools build system
[chaz/yoink] / src / stlplus / strings / string_inf.cpp
diff --git a/src/stlplus/strings/string_inf.cpp b/src/stlplus/strings/string_inf.cpp
new file mode 100644 (file)
index 0000000..1734335
--- /dev/null
@@ -0,0 +1,536 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   String conversion functions for the infinite precision integer type inf\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// can be excluded from the build to break the dependency on the portability library\r
+#ifndef NO_STLPLUS_INF\r
+\r
+#include "string_inf.hpp"\r
+#include "string_basic.hpp"\r
+#include <ctype.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz";\r
+  static int from_char [] = \r
+  {\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string inf_to_string(const stlplus::inf& data, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    std::string result;\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value");\r
+    inf local_i = data;\r
+    // untangle all the options\r
+    bool hashed = false;\r
+    bool binary = false;\r
+    bool octal = false;\r
+    bool hex = false;\r
+    switch(display)\r
+    {\r
+    case radix_none:\r
+      break;\r
+    case radix_hash_style:\r
+      hashed = radix != 10;\r
+      break;\r
+    case radix_hash_style_all:\r
+      hashed = true;\r
+      break;\r
+    case radix_c_style:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      break;\r
+    case radix_c_style_or_hash:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      else if (radix != 10)\r
+        hashed = true;\r
+      break;\r
+    default:\r
+      throw std::invalid_argument("invalid radix display value");\r
+    }\r
+    // create constants of the same type as the template parameter to avoid type mismatches\r
+    const inf t_zero(0);\r
+    const inf t_radix(radix);\r
+    // the C representations for binary, octal and hex use 2's-complement representation\r
+    // all other represenations use sign-magnitude\r
+    if (hex || octal || binary)\r
+    {\r
+      // bit-pattern representation\r
+      // this is the binary representation optionally shown in octal or hex\r
+      // first generate the binary by masking the bits\r
+      for (unsigned j = local_i.bits(); j--; )\r
+        result += (local_i.bit(j) ? '1' : '0');\r
+      // the result is now the full width of the type - e.g. int will give a 32-bit result\r
+      // now interpret this as either binary, octal or hex and add the prefix\r
+      if (binary)\r
+      {\r
+        // the result is already binary - but the width may be wrong\r
+        // if this is still smaller than the width field, sign extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        while (result.size() < width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > width)\r
+        {\r
+          // do not trim to less than 1 bit (sign only)\r
+          if (result.size() <= 1) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0b");\r
+      }\r
+      else if (octal)\r
+      {\r
+        // the result is currently binary - but before converting get the width right\r
+        // the width is expressed in octal digits so make the binary 3 times this\r
+        // if this is still smaller than the width field, sign extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier\r
+        while (result.size() < 3*width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > 3*width)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 3 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to octal\r
+        std::string octal_result;\r
+        for (unsigned i = 0; i < result.size()/3; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*3] == '0')\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '0';\r
+              else\r
+                octal_result += '1';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '2';\r
+              else\r
+                octal_result += '3';\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '4';\r
+              else\r
+                octal_result += '5';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '6';\r
+              else\r
+                octal_result += '7';\r
+            }\r
+          }\r
+        }\r
+        result = octal_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0");\r
+      }\r
+      else\r
+      {\r
+        // similar to octal\r
+        while (result.size() < 4*width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > 4*width)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 4 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to hex\r
+        std::string hex_result;\r
+        for (unsigned i = 0; i < result.size()/4; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*4] == '0')\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '0';\r
+                else\r
+                  hex_result += '1';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '2';\r
+                else\r
+                  hex_result += '3';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '4';\r
+                else\r
+                  hex_result += '5';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '6';\r
+                else\r
+                  hex_result += '7';\r
+              }\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '8';\r
+                else\r
+                  hex_result += '9';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'a';\r
+                else\r
+                  hex_result += 'b';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'c';\r
+                else\r
+                  hex_result += 'd';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'e';\r
+                else\r
+                  hex_result += 'f';\r
+              }\r
+            }\r
+          }\r
+        }\r
+        result = hex_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0x");\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // convert to sign-magnitude\r
+      // the representation is:\r
+      // [radix#][sign]magnitude\r
+      bool negative = local_i.negative();\r
+      local_i.abs();\r
+      // create a representation of the magnitude by successive division\r
+      do\r
+      {\r
+        std::pair<inf,inf> divided = local_i.divide(t_radix);\r
+        unsigned remainder = divided.second.to_unsigned();\r
+        char digit = to_char[remainder];\r
+        result.insert((std::string::size_type)0, 1, digit);\r
+        local_i = divided.first;\r
+      }\r
+      while(!local_i.zero() || result.size() < width);\r
+      // add the prefixes\r
+      // add a sign only for negative values\r
+      if (negative)\r
+        result.insert((std::string::size_type)0, 1, '-');\r
+      // then prefix everything with the radix if the hashed representation was requested\r
+      if (hashed)\r
+        result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#");\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions FROM string\r
+\r
+  inf string_to_inf(const std::string& str, unsigned radix) throw(std::invalid_argument)\r
+  {\r
+    inf result;\r
+    if (radix != 0 && (radix < 2 || radix > 36))\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    unsigned i = 0;\r
+    // the radix passed as a parameter is just the default - it can be\r
+    // overridden by either the C prefix or the hash prefix\r
+    // Note: a leading zero is the C-style prefix for octal - I only make this\r
+    // override the default when the default radix is not specified\r
+    // first check for a C-style prefix\r
+    bool c_style = false;\r
+    if (i < str.size() && str[i] == '0')\r
+    {\r
+      // binary or hex\r
+      if (i+1 < str.size() && tolower(str[i+1]) == 'x')\r
+      {\r
+        c_style = true;\r
+        radix = 16;\r
+        i += 2;\r
+      }\r
+      else if (i+1 < str.size() && tolower(str[i+1]) == 'b')\r
+      {\r
+        c_style = true;\r
+        radix = 2;\r
+        i += 2;\r
+      }\r
+      else if (radix == 0)\r
+      {\r
+        c_style = true;\r
+        radix = 8;\r
+        i += 1;\r
+      }\r
+    }\r
+    // now check for a hash-style prefix if a C-style prefix was not found\r
+    if (i == 0)\r
+    {\r
+      // scan for the sequence {digits}#\r
+      bool hash_found = false;\r
+      unsigned j = i;\r
+      for (; j < str.size(); j++)\r
+      {\r
+        if (!isdigit(str[j]))\r
+        {\r
+          if (str[j] == '#')\r
+            hash_found = true;\r
+          break;\r
+        }\r
+      }\r
+      if (hash_found)\r
+      {\r
+        // use the hash prefix to define the radix\r
+        // i points to the start of the radix and j points to the # character\r
+        std::string slice = str.substr(i, j-i);\r
+        radix = string_to_unsigned(slice);\r
+        i = j+1;\r
+      }\r
+    }\r
+    if (radix == 0)\r
+      radix = 10;\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value");\r
+    if (c_style)\r
+    {\r
+      // the C style formats are bit patterns not integer values - these need\r
+      // to be sign-extended to get the right value\r
+      std::string binary;\r
+      if (radix == 2)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += '0';\r
+            break;\r
+          case '1':\r
+            binary += '1';\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid binary character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      else if (radix == 8)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += "000";\r
+            break;\r
+          case '1':\r
+            binary += "001";\r
+            break;\r
+          case '2':\r
+            binary += "010";\r
+            break;\r
+          case '3':\r
+            binary += "011";\r
+            break;\r
+          case '4':\r
+            binary += "100";\r
+            break;\r
+          case '5':\r
+            binary += "101";\r
+            break;\r
+          case '6':\r
+            binary += "110";\r
+            break;\r
+          case '7':\r
+            binary += "111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid octal character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      else\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(tolower(str[j]))\r
+          {\r
+          case '0':\r
+            binary += "0000";\r
+            break;\r
+          case '1':\r
+            binary += "0001";\r
+            break;\r
+          case '2':\r
+            binary += "0010";\r
+            break;\r
+          case '3':\r
+            binary += "0011";\r
+            break;\r
+          case '4':\r
+            binary += "0100";\r
+            break;\r
+          case '5':\r
+            binary += "0101";\r
+            break;\r
+          case '6':\r
+            binary += "0110";\r
+            break;\r
+          case '7':\r
+            binary += "0111";\r
+            break;\r
+          case '8':\r
+            binary += "1000";\r
+            break;\r
+          case '9':\r
+            binary += "1001";\r
+            break;\r
+          case 'a':\r
+            binary += "1010";\r
+            break;\r
+          case 'b':\r
+            binary += "1011";\r
+            break;\r
+          case 'c':\r
+            binary += "1100";\r
+            break;\r
+          case 'd':\r
+            binary += "1101";\r
+            break;\r
+          case 'e':\r
+            binary += "1110";\r
+            break;\r
+          case 'f':\r
+            binary += "1111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid hex character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      // now convert the value\r
+      result.resize(binary.size());\r
+      for (unsigned j = 0; j < binary.size(); j++)\r
+        result.preset(binary.size() - j - 1, binary[j] == '1');\r
+    }\r
+    else\r
+    {\r
+      // now scan for a sign and find whether this is a negative number\r
+      bool negative = false;\r
+      if (i < str.size())\r
+      {\r
+        switch (str[i])\r
+        {\r
+        case '-':\r
+          negative = true;\r
+          i++;\r
+          break;\r
+        case '+':\r
+          i++;\r
+          break;\r
+        }\r
+      }\r
+      for (; i < str.size(); i++)\r
+      {\r
+        result *= inf(radix);\r
+        int ch = from_char[(unsigned char)str[i]] ;\r
+        if (ch == -1)\r
+          throw std::invalid_argument("invalid character in string " + str + " for radix " + unsigned_to_string(radix));\r
+        result += inf(ch);\r
+      }\r
+      if (negative)\r
+        result.negate();\r
+    }\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
This page took 0.03728 seconds and 4 git commands to generate.