diff --git a/Foundation/CPString.j b/Foundation/CPString.j index 310a9aecc..3de11401b 100644 --- a/Foundation/CPString.j +++ b/Foundation/CPString.j @@ -641,185 +641,3 @@ var CPStringHashes = new objj_dictionary(); String.prototype.isa = CPString; - - -// sprintf: - -var sprintfFormatRegex = new RegExp("([^%]+|%[\\+\\-\\ \\#0]*[0-9\\*]*(.[0-9\\*]+)?[hlL]?[cbBdieEfgGosuxXpn%@])", "g"); -var sprintfTagRegex = new RegExp("(%)([\\+\\-\\ \\#0]*)([0-9\\*]*)((.[0-9\\*]+)?)([hlL]?)([cbBdieEfgGosuxXpn%@])"); - -/*! - Creates a new string using C printf-style formatting. First argument should be a constant format string, like ' "float val = %f" ', remaining arguments should be the variables to print the values of, comma-separated. - @param format the format to be used, printf-style - @return the initialized CPString -*/ -function sprintf(format) -{ - var format = arguments[0], - tokens = format.match(sprintfFormatRegex), - index = 0, - result = "", - arg = 1; - - for (var i = 0; i < tokens.length; i++) - { - var t = tokens[i]; - if (format.substring(index, index + t.length) != t) - { - return result; - } - index += t.length; - - if (t.charAt(0) != "%") - { - result += t; - } - else - { - var subtokens = t.match(sprintfTagRegex); - if (subtokens.length != 8 || subtokens[0] != t) - { - return result; - } - - var percentSign = subtokens[1], - flags = subtokens[2], - widthString = subtokens[3], - precisionString = subtokens[4], - length = subtokens[6], - specifier = subtokens[7]; - - var width = null; - if (widthString == "*") - width = arguments[arg++]; - else if (widthString != "") - width = Number(widthString); - - var precision = null; - if (precisionString == ".*") - precision = arguments[arg++]; - else if (precisionString != "") - precision = Number(precisionString.substring(1)); - - var leftJustify = (flags.indexOf("-") >= 0); - var padZeros = (flags.indexOf("0") >= 0); - - var subresult = ""; - - if (RegExp("[bBdiufeExXo]").test(specifier)) - { - var num = Number(arguments[arg++]); - - var sign = ""; - if (num < 0) - { - sign = "-"; - } - else - { - if (flags.indexOf("+") >= 0) - sign = "+"; - else if (flags.indexOf(" ") >= 0) - sign = " "; - } - - if (specifier == "d" || specifier == "i" || specifier == "u") - { - var number = String(Math.abs(Math.floor(num))); - - subresult = sprintf_justify(sign, "", number, "", width, leftJustify, padZeros) - } - - if (specifier == "f") - { - var number = String((precision != null) ? Math.abs(num).toFixed(precision) : Math.abs(num)); - var suffix = (flags.indexOf("#") >= 0 && number.indexOf(".") < 0) ? "." : ""; - - subresult = sprintf_justify(sign, "", number, suffix, width, leftJustify, padZeros); - } - - if (specifier == "e" || specifier == "E") - { - var number = String(Math.abs(num).toExponential(precision != null ? precision : 21)); - var suffix = (flags.indexOf("#") >= 0 && number.indexOf(".") < 0) ? "." : ""; - - subresult = sprintf_justify(sign, "", number, suffix, width, leftJustify, padZeros); - } - - if (specifier == "x" || specifier == "X") - { - var number = String(Math.abs(num).toString(16)); - var prefix = (flags.indexOf("#") >= 0 && num != 0) ? "0x" : ""; - - subresult = sprintf_justify(sign, prefix, number, "", width, leftJustify, padZeros); - } - - if (specifier == "b" || specifier == "B") - { - var number = String(Math.abs(num).toString(2)); - var prefix = (flags.indexOf("#") >= 0 && num != 0) ? "0b" : ""; - - subresult = sprintf_justify(sign, prefix, number, "", width, leftJustify, padZeros); - } - - if (specifier == "o") - { - var number = String(Math.abs(num).toString(8)); - var prefix = (flags.indexOf("#") >= 0 && num != 0) ? "0" : ""; - - subresult = sprintf_justify(sign, prefix, number, "", width, leftJustify, padZeros); - } - - if (RegExp("[A-Z]").test(specifier)) - subresult = subresult.toUpperCase(); - else - subresult = subresult.toLowerCase(); - } - else - { - var subresult = ""; - - if (specifier == "%") - subresult = "%"; - else if (specifier == "c") - subresult = String(arguments[arg++]).charAt(0); - else if (specifier == "s" || specifier == "@") - subresult = String(arguments[arg++]); - else if (specifier == "p" || specifier == "n") - { - arg++; - subresult = ""; - } - - subresult = sprintf_justify("", "", subresult, "", width, leftJustify, false); - } - - result += subresult; - } - } - return result; -} - -var sprintf_justify = function(sign, prefix, string, suffix, width, leftJustify, padZeros) -{ - var length = (sign.length + prefix.length + string.length + suffix.length); - if (leftJustify) - { - return sign + prefix + string + suffix + sprintf_pad(width - length, " "); - } - else - { - if (padZeros) - return sign + prefix + sprintf_pad(width - length, "0") + string + suffix; - else - return sprintf_pad(width - length, " ") + sign + prefix + string + suffix; - } -} - -var sprintf_pad = function(n, ch) -{ - var result = ""; - for (var i = 0; i < n; i++) - result += ch; - return result; -} diff --git a/Objective-J/build.xml b/Objective-J/build.xml index 7cb2cf127..29ba43aa5 100644 --- a/Objective-J/build.xml +++ b/Objective-J/build.xml @@ -9,6 +9,7 @@ + diff --git a/Objective-J/utilities.js b/Objective-J/utilities.js new file mode 100644 index 000000000..09d499b3e --- /dev/null +++ b/Objective-J/utilities.js @@ -0,0 +1,267 @@ +// sprintf: + +var _sprintfFormatRegex = new RegExp("([^%]+|%[\\+\\-\\ \\#0]*[0-9\\*]*(.[0-9\\*]+)?[hlL]?[cbBdieEfgGosuxXpn%@])", "g"); +var _sprintfTagRegex = new RegExp("(%)([\\+\\-\\ \\#0]*)([0-9\\*]*)((.[0-9\\*]+)?)([hlL]?)([cbBdieEfgGosuxXpn%@])"); + +function sprintf(format) +{ + var format = arguments[0], + tokens = format.match(_sprintfFormatRegex), + index = 0, + result = "", + arg = 1; + + for (var i = 0; i < tokens.length; i++) + { + var t = tokens[i]; + if (format.substring(index, index + t.length) != t) + { + return result; + } + index += t.length; + + if (t.charAt(0) != "%") + { + result += t; + } + else + { + var subtokens = t.match(_sprintfTagRegex); + if (subtokens.length != 8 || subtokens[0] != t) + { + return result; + } + + var percentSign = subtokens[1], + flags = subtokens[2], + widthString = subtokens[3], + precisionString = subtokens[4], + length = subtokens[6], + specifier = subtokens[7]; + + var width = null; + if (widthString == "*") + width = arguments[arg++]; + else if (widthString != "") + width = Number(widthString); + + var precision = null; + if (precisionString == ".*") + precision = arguments[arg++]; + else if (precisionString != "") + precision = Number(precisionString.substring(1)); + + var leftJustify = (flags.indexOf("-") >= 0); + var padZeros = (flags.indexOf("0") >= 0); + + var subresult = ""; + + if (RegExp("[bBdiufeExXo]").test(specifier)) + { + var num = Number(arguments[arg++]); + + var sign = ""; + if (num < 0) + { + sign = "-"; + } + else + { + if (flags.indexOf("+") >= 0) + sign = "+"; + else if (flags.indexOf(" ") >= 0) + sign = " "; + } + + if (specifier == "d" || specifier == "i" || specifier == "u") + { + var number = String(Math.abs(Math.floor(num))); + + subresult = _sprintf_justify(sign, "", number, "", width, leftJustify, padZeros) + } + + if (specifier == "f") + { + var number = String((precision != null) ? Math.abs(num).toFixed(precision) : Math.abs(num)); + var suffix = (flags.indexOf("#") >= 0 && number.indexOf(".") < 0) ? "." : ""; + + subresult = _sprintf_justify(sign, "", number, suffix, width, leftJustify, padZeros); + } + + if (specifier == "e" || specifier == "E") + { + var number = String(Math.abs(num).toExponential(precision != null ? precision : 21)); + var suffix = (flags.indexOf("#") >= 0 && number.indexOf(".") < 0) ? "." : ""; + + subresult = _sprintf_justify(sign, "", number, suffix, width, leftJustify, padZeros); + } + + if (specifier == "x" || specifier == "X") + { + var number = String(Math.abs(num).toString(16)); + var prefix = (flags.indexOf("#") >= 0 && num != 0) ? "0x" : ""; + + subresult = _sprintf_justify(sign, prefix, number, "", width, leftJustify, padZeros); + } + + if (specifier == "b" || specifier == "B") + { + var number = String(Math.abs(num).toString(2)); + var prefix = (flags.indexOf("#") >= 0 && num != 0) ? "0b" : ""; + + subresult = _sprintf_justify(sign, prefix, number, "", width, leftJustify, padZeros); + } + + if (specifier == "o") + { + var number = String(Math.abs(num).toString(8)); + var prefix = (flags.indexOf("#") >= 0 && num != 0) ? "0" : ""; + + subresult = _sprintf_justify(sign, prefix, number, "", width, leftJustify, padZeros); + } + + if (RegExp("[A-Z]").test(specifier)) + subresult = subresult.toUpperCase(); + else + subresult = subresult.toLowerCase(); + } + else + { + var subresult = ""; + + if (specifier == "%") + subresult = "%"; + else if (specifier == "c") + subresult = String(arguments[arg++]).charAt(0); + else if (specifier == "s" || specifier == "@") + subresult = String(arguments[arg++]); + else if (specifier == "p" || specifier == "n") + { + arg++; + subresult = ""; + } + + subresult = _sprintf_justify("", "", subresult, "", width, leftJustify, false); + } + + result += subresult; + } + } + return result; +} + +var _sprintf_justify = function(sign, prefix, string, suffix, width, leftJustify, padZeros) +{ + var length = (sign.length + prefix.length + string.length + suffix.length); + if (leftJustify) + { + return sign + prefix + string + suffix + _sprintf_pad(width - length, " "); + } + else + { + if (padZeros) + return sign + prefix + _sprintf_pad(width - length, "0") + string + suffix; + else + return _sprintf_pad(width - length, " ") + sign + prefix + string + suffix; + } +} + +var _sprintf_pad = function(n, ch) +{ + var result = ""; + for (var i = 0; i < n; i++) + result += ch; + return result; +} + +// Base64 encoding and decoding + +var base64_map_to = [ + "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","+","/","="], + base64_map_from = []; + +for (var i = 0; i < base64_map_to.length; i++) + base64_map_from[base64_map_to[i].charCodeAt(0)] = i; + +function base64_decode_to_array(input, strip) +{ + if (strip) + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + var pad = (input[input.length-1] == "=" ? 1 : 0) + (input[input.length-2] == "=" ? 1 : 0), + length = input.length, + output = []; + + var i = 0; + while (i < length) + { + var bits = (base64_map_from[input.charCodeAt(i++)] << 18) | + (base64_map_from[input.charCodeAt(i++)] << 12) | + (base64_map_from[input.charCodeAt(i++)] << 6) | + (base64_map_from[input.charCodeAt(i++)]); + + output.push((bits & 0xFF0000) >> 16); + output.push((bits & 0xFF00) >> 8); + output.push(bits & 0xFF); + } + + // strip "=" padding from end + if (pad > 0) + return output.slice(0, -1 * pad); + + return output; +} + +function base64_encode_array(input) +{ + var pad = (3 - (input.length % 3)) % 3, + length = input.length + pad, + output = []; + + // pad with nulls + if (pad > 0) input.push(0); + if (pad > 1) input.push(0); + + var i = 0; + while (i < length) + { + var bits = (input[i++] << 16) | + (input[i++] << 8) | + (input[i++]); + + output.push(base64_map_to[(bits & 0xFC0000) >> 18]); + output.push(base64_map_to[(bits & 0x3F000) >> 12]); + output.push(base64_map_to[(bits & 0xFC0) >> 6]); + output.push(base64_map_to[bits & 0x3F]); + } + + // pad with "=" and revert array to previous state + if (pad > 0) + { + output[output.length-1] = "="; + input.pop(); + } + if (pad > 1) + { + output[output.length-2] = "="; + input.pop(); + } + + return output.join(""); +} + +function base64_decode_to_string(input, strip) +{ + return String.fromCharCode.apply(null, base64_decode_to_array(input, strip)); +} + +function base64_encode_string(input) +{ + var temp = []; + for (var i = 0; i < input.length; i++) + temp.push(input.charCodeAt(i)); + + return base64_encode_array(temp); +} diff --git a/Tests/Objective-J/base64Test.j b/Tests/Objective-J/base64Test.j new file mode 100644 index 000000000..e898fce94 --- /dev/null +++ b/Tests/Objective-J/base64Test.j @@ -0,0 +1,49 @@ +var base64TestStrings = [ + ["leasure.", "bGVhc3VyZS4="], + ["easure.", "ZWFzdXJlLg=="], + ["asure.", "YXN1cmUu"], + ["sure.", "c3VyZS4="] +]; + +@implementation base64Test : OJTestCase + +- (void)test_base64_encode_string +{ + for (var i = 0; i < base64TestStrings.length; i++) + [self assert:base64_encode_string(base64TestStrings[i][0]) equals:base64TestStrings[i][1]]; +} + +- (void)test_base64_encode_array +{ + for (var i = 0; i < base64TestStrings.length; i++) + { + var input = []; + for (var j = 0; j < base64TestStrings[i][0].length; j++) + input.push(base64TestStrings[i][0].charCodeAt(j)); + [self assert:base64_encode_array(input) equals:base64TestStrings[i][1]]; + } +} + +- (void)test_base64_decode_to_string +{ + for (var i = 0; i < base64TestStrings.length; i++) + { + var result = base64_decode_to_string(base64TestStrings[i][1]), + expected = base64TestStrings[i][0]; + [self assert:result equals:expected]; + } +} + +- (void)test_base64_decode_to_array +{ + for (var i = 0; i < base64TestStrings.length; i++) + { + var result = base64_decode_to_array(base64TestStrings[i][1]), + expected = base64TestStrings[i][0]; + + for (var j = 0; j < expected.length || j < result.length; j++) + [self assert:result[j] equals:expected.charCodeAt(j)]; + } +} + +@end diff --git a/Tests/Objective-J/sprintfTest.j b/Tests/Objective-J/sprintfTest.j new file mode 100644 index 000000000..24c1696a3 --- /dev/null +++ b/Tests/Objective-J/sprintfTest.j @@ -0,0 +1,19 @@ +// TODO: add many many more of these... +var sprintfTestCases = [ + [["[%@]", "hello world"], "[hello world]"], + [["[%d]", 123], "[123]"], + [["[%f]", 123.1234], "[123.1234]"], + [["[%d]", 123.1234], "[123]"] +]; + +@implementation sprintfTest : OJTestCase + +- (void)test_sprintf +{ + for (var i = 0; i < sprintfTestCases.length; i++) + { + [self assert:sprintf.apply(null, sprintfTestCases[i][0]) equals:sprintfTestCases[i][1]]; + } +} + +@end diff --git a/build.xml b/build.xml index c5ccca0dd..117838c16 100644 --- a/build.xml +++ b/build.xml @@ -79,6 +79,7 @@ +