blob: 319519eae3b909420900ce7915d2167d64cfb108 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
library naming;
import 'dart:convert' show UTF8;
// TODO(aghassemi): Move these naming utilities out of Syncbase once we
// implement a Dart Vanadium library.
// TODO(sadovsky): Make this function support more than two input names, and
// clean the resulting string the same way we do in Go and other languages.
String join(String a, String b) {
return a + '/' + b;
}
// Makes a string representable as a name element by escaping slashes.
String encodeAsNameElement(String name) {
return escape(name, '/');
}
// Decodes an encoded name element.
// It throws exception when encountering wrongly encoded names.
// Note that this is more than the inverse of EncodeAsNameElement since it
// can handle more hex encodings than / and %.
// This is intentional since we'll most likely want to add other letters to
// the set to be encoded.
String decodeFromNameElement(String name) {
return unescape(name);
}
// Encodes a string replacing the characters in <special> and % with a
// %<hex> escape.
String escape(String text, String special) {
const String encodedPecent = '%25';
const String percent = '%';
// Replace % with %25 first.
var encodedText = text.replaceAll(percent, encodedPecent);
// For all characters in special, replace occurrences with their hex encoding.
// Note that we want to support any Unicode character in special, single-byte
// or not. For example a three-byte 읔 character, becomes %EC%9D%94.
for (var i = 0; i < special.length; i++) {
var char = special[i];
if (char == '%') {
// Ignore % in special, we have already escaped %.
continue;
}
var bytes = UTF8.encoder.convert(char);
var hex = '';
for (var byte in bytes) {
hex += percent;
hex += _byteToHex(byte);
}
encodedText = encodedText.replaceAll(char, hex);
}
return encodedText;
}
// Decodes %<hex> encodings in a string into the relevant character.
// It throws exception when encountering wrongly encoded text.
String unescape(String text) {
// Note that this function is a slightly modified version of _uriDecode() code
// in Dart sdk/lib/core/uri.dart (https://goo.gl/1ppJIj).
// The biggest difference is that, unlike _uriDecode(), our code DOES NOT
// expect non-ASCII characters to have been percent encoded in text.
// Dart's _uriDecode() however expects character above 127 to have been
// percent encoded or it will throw an argument exception when it encounters a
// character above 127.
const int percent = 0x25;
// First check whether there are any characters which need special handling.
bool notEncoded = true;
for (int i = 0; i < text.length && notEncoded; i++) {
var codeUnit = text.codeUnitAt(i);
notEncoded = codeUnit != percent;
}
if (notEncoded) {
return text;
}
List<int> bytes = new List();
for (int i = 0; i < text.length; i++) {
var codeUnit = text.codeUnitAt(i);
if (codeUnit == percent) {
if (i + 3 > text.length) {
throw new ArgumentError('Truncated or malformed encoded string');
}
bytes.add(_hexCharPairToByte(text, i + 1));
i += 2;
} else {
bytes.addAll(UTF8.encoder.convert(text[i]));
}
}
return UTF8.decode(bytes);
}
// Converts a byte to 0 padded hex string.
// Note that this function is the same as byteToHex() code
// in Dart sdk/lib/core/uri.dart (https://goo.gl/1ppJIj)
String _byteToHex(int byte) {
const String hex = '0123456789ABCDEF';
var buffer = new StringBuffer();
buffer.writeCharCode(hex.codeUnitAt(byte >> 4));
buffer.writeCharCode(hex.codeUnitAt(byte & 0x0f));
return buffer.toString();
}
// Converts a hex string to a byte.
// Note that this function is the same code as _hexCharPairToByte()
// in Dart sdk/lib/core/uri.dart (https://goo.gl/1ppJIj)
int _hexCharPairToByte(String s, int pos) {
int byte = 0;
for (int i = 0; i < 2; i++) {
var charCode = s.codeUnitAt(pos + i);
if (0x30 <= charCode && charCode <= 0x39) {
byte = byte * 16 + charCode - 0x30;
} else {
// Check ranges A-F (0x41-0x46) and a-f (0x61-0x66).
charCode |= 0x20;
if (0x61 <= charCode && charCode <= 0x66) {
byte = byte * 16 + charCode - 0x57;
} else {
throw new ArgumentError("Truncated or malformed encoded string");
}
}
}
return byte;
}