OSDN Git Service

Support upcase table compression.
authorrelan <relan@users.noreply.github.com>
Fri, 25 Mar 2016 10:06:25 +0000 (13:06 +0300)
committerrelan <relan@users.noreply.github.com>
Fri, 3 Jun 2016 05:11:00 +0000 (08:11 +0300)
Upcase table can use RLE for identity-mapped characters.

libexfat/exfat.h
libexfat/exfatfs.h
libexfat/lookup.c
libexfat/mount.c
libexfat/node.c
libexfat/utils.c

index 97d3692..dd1b2ca 100644 (file)
@@ -97,8 +97,7 @@ struct exfat
 {
        struct exfat_dev* dev;
        struct exfat_super_block* sb;
-       le16_t* upcase;
-       size_t upcase_chars;
+       uint16_t* upcase;
        struct exfat_node* root;
        struct
        {
index eca2cac..29c7d22 100644 (file)
@@ -99,6 +99,8 @@ struct exfat_entry_bitmap                     /* allocated clusters bitmap */
 PACKED;
 STATIC_ASSERT(sizeof(struct exfat_entry_bitmap) == 32);
 
+#define EXFAT_UPCASE_CHARS 0x10000
+
 struct exfat_entry_upcase                      /* upper case translation table */
 {
        uint8_t type;                                   /* EXFAT_ENTRY_UPCASE */
index d5bb389..d738895 100644 (file)
@@ -61,10 +61,7 @@ struct exfat_node* exfat_readdir(struct exfat* ef, struct exfat_iterator* it)
 
 static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
 {
-       if (a >= ef->upcase_chars || b >= ef->upcase_chars)
-               return (int) a - (int) b;
-
-       return (int) le16_to_cpu(ef->upcase[a]) - (int) le16_to_cpu(ef->upcase[b]);
+       return (int) ef->upcase[a] - (int) ef->upcase[b];
 }
 
 static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
index 0d6ce9e..1daebd5 100644 (file)
@@ -376,5 +376,4 @@ void exfat_unmount(struct exfat* ef)
        ef->sb = NULL;
        free(ef->upcase);
        ef->upcase = NULL;
-       ef->upcase_chars = 0;
 }
index 4dd4dc6..6a93277 100644 (file)
@@ -227,6 +227,26 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum,
        return true;
 }
 
+static void decompress_upcase(uint16_t* output, const le16_t* source,
+               size_t size)
+{
+       size_t si;
+       size_t oi;
+
+       for (oi = 0; oi < EXFAT_UPCASE_CHARS; oi++)
+               output[oi] = oi;
+
+       for (si = 0, oi = 0; si < size && oi < EXFAT_UPCASE_CHARS; si++)
+       {
+               uint16_t ch = le16_to_cpu(source[si]);
+
+               if (ch == 0xffff && si + 1 < size)      /* indicates a run */
+                       oi += le16_to_cpu(source[++si]);
+               else
+                       output[oi++] = ch;
+       }
+}
+
 /*
  * Reads one entry in directory at position pointed by iterator and fills
  * node structure.
@@ -247,6 +267,8 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
        uint16_t reference_checksum = 0;
        uint16_t actual_checksum = 0;
        uint64_t valid_size = 0;
+       uint64_t upcase_size = 0;
+       le16_t* upcase_comp = NULL;
 
        *node = NULL;
 
@@ -371,33 +393,48 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                                                le32_to_cpu(upcase->start_cluster));
                                goto error;
                        }
-                       if (le64_to_cpu(upcase->size) == 0 ||
-                               le64_to_cpu(upcase->size) > 0xffff * sizeof(uint16_t) ||
-                               le64_to_cpu(upcase->size) % sizeof(uint16_t) != 0)
+                       upcase_size = le64_to_cpu(upcase->size);
+                       if (upcase_size == 0 ||
+                               upcase_size > EXFAT_UPCASE_CHARS * sizeof(uint16_t) ||
+                               upcase_size % sizeof(uint16_t) != 0)
                        {
                                exfat_error("bad upcase table size (%"PRIu64" bytes)",
-                                               le64_to_cpu(upcase->size));
+                                               upcase_size);
                                goto error;
                        }
-                       ef->upcase = malloc(le64_to_cpu(upcase->size));
-                       if (ef->upcase == NULL)
+                       upcase_comp = malloc(upcase_size);
+                       if (upcase_comp == NULL)
                        {
                                exfat_error("failed to allocate upcase table (%"PRIu64" bytes)",
-                                               le64_to_cpu(upcase->size));
+                                               upcase_size);
                                rc = -ENOMEM;
                                goto error;
                        }
-                       ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t);
 
-                       if (exfat_pread(ef->dev, ef->upcase, le64_to_cpu(upcase->size),
+                       /* read compressed upcase table */
+                       if (exfat_pread(ef->dev, upcase_comp, upcase_size,
                                        exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))) < 0)
                        {
+                               free(upcase_comp);
                                exfat_error("failed to read upper case table "
                                                "(%"PRIu64" bytes starting at cluster %#x)",
-                                               le64_to_cpu(upcase->size),
+                                               upcase_size,
                                                le32_to_cpu(upcase->start_cluster));
                                goto error;
                        }
+
+                       /* decompress upcase table */
+                       ef->upcase = calloc(EXFAT_UPCASE_CHARS, sizeof(uint16_t));
+                       if (ef->upcase == NULL)
+                       {
+                               free(upcase_comp);
+                               exfat_error("failed to allocate decompressed upcase table");
+                               rc = -ENOMEM;
+                               goto error;
+                       }
+                       decompress_upcase(ef->upcase, upcase_comp,
+                                       upcase_size / sizeof(uint16_t));
+                       free(upcase_comp);
                        break;
 
                case EXFAT_ENTRY_BITMAP:
index 388f360..f8684a6 100644 (file)
@@ -124,8 +124,7 @@ le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name)
                uint16_t c = le16_to_cpu(name[i]);
 
                /* convert to upper case */
-               if (c < ef->upcase_chars)
-                       c = le16_to_cpu(ef->upcase[c]);
+               c = ef->upcase[c];
 
                hash = ((hash << 15) | (hash >> 1)) + (c & 0xff);
                hash = ((hash << 15) | (hash >> 1)) + (c >> 8);