ifcfg-rh: change algorithm for svUnescape

The previous algorithm had runtime complexity O(n^2). Change
it to O(2*n).

Signed-off-by: Thomas Haller <thaller@redhat.com>
This commit is contained in:
Thomas Haller 2014-01-24 22:12:42 +01:00
parent ff350c04c0
commit c9e241e2f9
2 changed files with 158 additions and 19 deletions

View file

@ -113,30 +113,63 @@ svCreateFile(const char *name)
/* remove escaped characters in place */
void
svUnescape(char *s) {
int len, i;
svUnescape (char *s)
{
size_t len, idx_rd = 0, idx_wr = 0;
char c;
len = strlen(s);
if (len >= 2 && (s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
i = len - 2;
if (i == 0)
s[0] = '\0';
else {
memmove(s, s+1, i);
s[i+1] = '\0';
len = i;
len = strlen(s);
if (len < 2) {
if (s[0] == '\\')
s[0] = '\0';
return;
}
}
for (i = 0; i < len; i++) {
if (s[i] == '\\') {
memmove(s+i, s+i+1, len-(i+1));
len--;
if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
if (len == 2) {
s[0] = '\0';
return;
}
if (len == 3) {
if (s[1] == '\\') {
s[0] = '\0';
} else {
s[0] = s[1];
s[1] = '\0';
}
return;
}
s[--len] = '\0';
idx_rd = 1;
} else {
/* seek for the first escape... */
char *p = strchr (s, '\\');
if (!p)
return;
if (p[1] == '\0') {
p[0] = '\0';
return;
}
idx_wr = idx_rd = (p - s);
}
s[len] = '\0';
}
/* idx_rd points to the first escape. Walk the string and shift the
* characters from idx_rd to idx_wr. */
while ((c = s[idx_rd++])) {
if (c == '\\') {
if (s[idx_rd] == '\0') {
s[idx_wr] = '\0';
return;
}
s[idx_wr++] = s[idx_rd++];
continue;
}
s[idx_wr++] = c;
}
s[idx_wr] = '\0';
}
/* create a new string with all necessary characters escaped.
* caller must free returned string
*/

View file

@ -13475,6 +13475,110 @@ test_write_team_port (void)
g_object_unref (reread);
}
/* Old algorithm for "remove escaped characters in place".
*
* This function is obsolete because it has O(n^2) runtime
* complexity and got replaced. Keep it here for testing,
* that both functions behave identical.
**/
static void
svUnescape_On2 (char *s)
{
int len, i;
len = strlen(s);
if (len >= 2 && (s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
i = len - 2;
if (i == 0)
s[0] = '\0';
else {
memmove(s, s+1, i);
s[i+1] = '\0';
len = i;
}
}
for (i = 0; i < len; i++) {
if (s[i] == '\\') {
memmove(s+i, s+i+1, len-(i+1));
len--;
}
s[len] = '\0';
}
}
static void
test_svUnescape_assert (const char *str)
{
char *s1 = g_strdup (str);
char *s2 = g_strdup (str);
svUnescape (s1);
svUnescape_On2 (s2);
g_assert_cmpstr (s1, ==, s2);
g_free (s1);
g_free (s2);
}
static void
test_svUnescape ()
{
int len, repeat, i, k;
GRand *rand = g_rand_new ();
guint32 seed = g_random_int ();
g_rand_set_seed (rand, seed);
test_svUnescape_assert ("");
test_svUnescape_assert ("'");
test_svUnescape_assert ("\"");
test_svUnescape_assert ("\\");
test_svUnescape_assert ("x");
test_svUnescape_assert (" ");
test_svUnescape_assert ("' '");
test_svUnescape_assert ("'x'");
test_svUnescape_assert ("\'some string\'");
test_svUnescape_assert ("Bob outside LAN");
test_svUnescape_assert ("{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }");
for (len = 1; len < 25; len++) {
char *s = g_new0 (char, len+1);
for (repeat = 0; repeat < MAX (4*len, 20); repeat++) {
/* fill the entire string with random. */
for (i = 0; i < len; i++)
s[i] = g_rand_int (rand);
/* randomly place escape characters into the string */
k = g_rand_int (rand) % (len);
while (k-- > 0)
s[g_rand_int (rand) % len] = '\\';
if (len > 1) {
/* quote the string. */
k = g_rand_int (rand) % (10);
if (k < 4) {
char quote = k < 2 ? '"' : '\'';
s[0] = quote;
s[len-1] = quote;
}
}
/*g_message (">>%s<<", s);*/
test_svUnescape_assert (s);
}
g_free (s);
}
g_rand_free (rand);
}
#define TEST_IFCFG_WIFI_OPEN_SSID_BAD_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex"
#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted"
#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-hex"
@ -13506,6 +13610,8 @@ int main (int argc, char **argv)
g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
g_test_add_func (TPATH "svUnescape", test_svUnescape);
g_test_add_func (TPATH "unmanaged", test_read_unmanaged);
g_test_add_func (TPATH "unmanaged-unrecognized", test_read_unmanaged_unrecognized);
g_test_add_func (TPATH "unrecognized", test_read_unrecognized);