Merge "liblog: event_tag_map use unordered_map"
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index e8e0335..1155422 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2007-2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,362 +24,106 @@
 #include <string.h>
 #include <sys/mman.h>
 
-#include <android/log.h>
+#include <experimental/string_view>
+#include <functional>
+#include <unordered_map>
+
 #include <log/event_tag_map.h>
 
 #include "log_portability.h"
 
 #define OUT_TAG "EventTagMap"
 
-/*
- * Single entry.
- */
-typedef struct EventTag {
-    uint32_t tagIndex;
-    char*    tagStr;
-    size_t   tagLen;
-    char*    fmtStr;
-    size_t   fmtLen;
-} EventTag;
+typedef std::experimental::string_view MapString;
 
-/*
- * Map.
- */
-struct EventTagMap {
-    /* memory-mapped source file; we get strings from here */
-    void*           mapAddr;
-    size_t          mapLen;
+typedef std::pair<MapString, MapString> TagFmt;
 
-    /* array of event tags, sorted numerically by tag index */
-    EventTag*       tagArray;
-    int             numTags;
+template <> struct _LIBCPP_TYPE_VIS_ONLY std::hash<TagFmt>
+        : public std::unary_function<const TagFmt&, size_t> {
+    _LIBCPP_INLINE_VISIBILITY
+    size_t operator()(const TagFmt& __t) const _NOEXCEPT {
+        // Tag is typically unique.  Will cost us an extra 100ns for the
+        // unordered_map lookup if we instead did a hash that combined
+        // both of tag and fmt members, e.g.:
+        //
+        // return std::hash<MapString>()(__t.first) ^
+        //        std::hash<MapString>()(__t.second);
+        return std::hash<MapString>()(__t.first);
+    }
 };
 
-/* fwd */
-static int processFile(EventTagMap* map);
-static int countMapLines(const EventTagMap* map);
-static int parseMapLines(EventTagMap* map);
-static int scanTagLine(char** pData, EventTag* tag, int lineNum);
-static int sortTags(EventTagMap* map);
+// Map
+struct EventTagMap {
+    // memory-mapped source file; we get strings from here
+    void*  mapAddr;
+    size_t mapLen;
 
-/*
- * Open the map file and allocate a structure to manage it.
- *
- * We create a private mapping because we want to terminate the log tag
- * strings with '\0'.
- */
-LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName)
-{
-    EventTagMap* newTagMap;
-    off_t end;
-    int save_errno;
-    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
+private:
+    std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
 
-    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
-    if (fd < 0) {
-        save_errno = errno;
-        fprintf(stderr, "%s: unable to open map '%s': %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_errno;
-    }
+public:
+    EventTagMap() : mapAddr(NULL), mapLen(0) { }
 
-    end = lseek(fd, 0L, SEEK_END);
-    save_errno = errno;
-    (void) lseek(fd, 0L, SEEK_SET);
-    if (end < 0) {
-        fprintf(stderr, "%s: unable to seek map '%s' %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_close;
-    }
-
-    newTagMap = (EventTagMap*)calloc(1, sizeof(EventTagMap));
-    if (newTagMap == NULL) {
-        save_errno = errno;
-        goto fail_close;
-    }
-
-    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-    save_errno = errno;
-    close(fd);
-    fd = -1;
-    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
-        fprintf(stderr, "%s: mmap(%s) failed: %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_free;
-    }
-
-    newTagMap->mapLen = end;
-
-    if (processFile(newTagMap) != 0) goto fail_unmap;
-
-    return newTagMap;
-
-fail_unmap:
-    munmap(newTagMap->mapAddr, newTagMap->mapLen);
-    save_errno = EINVAL;
-fail_free:
-    free(newTagMap);
-fail_close:
-    close(fd);
-fail_errno:
-    errno = save_errno;
-fail:
-    return NULL;
-}
-
-/*
- * Close the map.
- */
-LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map)
-{
-    if (map == NULL) return;
-
-    munmap(map->mapAddr, map->mapLen);
-    free(map->tagArray);
-    free(map);
-}
-
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
-                                                         size_t *len,
-                                                         unsigned int tag)
-{
-    int lo = 0;
-    int hi = map->numTags - 1;
-
-    while (lo <= hi) {
-        int mid = (lo + hi) / 2;
-        int cmp = map->tagArray[mid].tagIndex - tag;
-
-        if (cmp < 0) {
-            /* tag is bigger */
-            lo = mid + 1;
-        } else if (cmp > 0) {
-            /* tag is smaller */
-            hi = mid - 1;
-        } else {
-            /* found */
-            if (len) *len = map->tagArray[mid].tagLen;
-            /*
-             * b/31456426 to check if gTest can detect copy-on-write issue
-             * add the following line to break us:
-             *     map->tagArray[mid].tagStr[map->tagArray[mid].tagLen] = '\0';
-             * or explicitly use deprecated android_lookupEventTag().
-             */
-            return map->tagArray[mid].tagStr;
+    ~EventTagMap() {
+        Idx2TagFmt.clear();
+        if (mapAddr) {
+            munmap(mapAddr, mapLen);
+            mapAddr = 0;
         }
     }
 
-    errno = ENOENT;
-    if (len) *len = 0;
-    return NULL;
-}
+    bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
+    const TagFmt* find(uint32_t tag) const;
+};
 
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
-    const EventTagMap* map, size_t *len, unsigned int tag)
-{
-    int lo = 0;
-    int hi = map->numTags - 1;
-
-    while (lo <= hi) {
-        int mid = (lo + hi) / 2;
-        int cmp = map->tagArray[mid].tagIndex - tag;
-
-        if (cmp < 0) {
-            /* tag is bigger */
-            lo = mid + 1;
-        } else if (cmp > 0) {
-            /* tag is smaller */
-            hi = mid - 1;
-        } else {
-            /* found */
-            if (len) *len = map->tagArray[mid].fmtLen;
-            return map->tagArray[mid].fmtStr;
+bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
+    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+    it = Idx2TagFmt.find(tag);
+    if (it != Idx2TagFmt.end()) {
+        if (verbose) {
+            fprintf(stderr,
+                    OUT_TAG ": duplicate tag entries %" PRIu32
+                        ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
+                    it->first,
+                    (int)it->second.first.length(), it->second.first.data(),
+                    (int)it->second.second.length(), it->second.second.data(),
+                    tag,
+                    (int)tagfmt.first.length(), tagfmt.first.data(),
+                    (int)tagfmt.second.length(), tagfmt.second.data());
         }
+        return false;
     }
 
-    errno = ENOENT;
-    if (len) *len = 0;
-    return NULL;
+    Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
+    return true;
 }
 
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
-                                                     unsigned int tag)
-{
-    size_t len;
-    const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+const TagFmt* EventTagMap::find(uint32_t tag) const {
+    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+    it = Idx2TagFmt.find(tag);
+    if (it == Idx2TagFmt.end()) return NULL;
+    return &(it->second);
+}
+
+// Scan one tag line.
+//
+// "*pData" should be pointing to the first digit in the tag number.  On
+// successful return, it will be pointing to the last character in the
+// tag line (i.e. the character before the start of the next line).
+//
+// Returns 0 on success, nonzero on failure.
+static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
     char* cp;
-
-    if (!tagStr) return tagStr;
-    cp = (char*)tagStr;
-    cp += len;
-    if (*cp) *cp = '\0'; /* Trigger copy on write :-( */
-    return tagStr;
-}
-
-/*
- * Crunch through the file, parsing the contents and creating a tag index.
- */
-static int processFile(EventTagMap* map)
-{
-    /* get a tag count */
-    map->numTags = countMapLines(map);
-    if (map->numTags < 0) {
-        errno = ENOENT;
-        return -1;
-    }
-
-    /* allocate storage for the tag index array */
-    map->tagArray = (EventTag*)calloc(1, sizeof(EventTag) * map->numTags);
-    if (map->tagArray == NULL) return -1;
-
-    /* parse the file, null-terminating tag strings */
-    if (parseMapLines(map) != 0) return -1;
-
-    /* sort the tags and check for duplicates */
-    if (sortTags(map) != 0) return -1;
-
-    return 0;
-}
-
-/*
- * Run through all lines in the file, determining whether they're blank,
- * comments, or possibly have a tag entry.
- *
- * This is a very "loose" scan.  We don't try to detect syntax errors here.
- * The later pass is more careful, but the number of tags found there must
- * match the number of tags found here.
- *
- * Returns the number of potential tag entries found.
- */
-static int countMapLines(const EventTagMap* map)
-{
-    const char* cp = (const char*) map->mapAddr;
-    const char* endp = cp + map->mapLen;
-    int numTags = 0;
-    int unknown = 1;
-
-    while (cp < endp) {
-        if (*cp == '\n') {
-            unknown = 1;
-        } else if (unknown) {
-            if (isdigit(*cp)) {
-                /* looks like a tag to me */
-                numTags++;
-                unknown = 0;
-            } else if (isspace(*cp)) {
-                /* might be leading whitespace before tag num, keep going */
-            } else {
-                /* assume comment; second pass can complain in detail */
-                unknown = 0;
-            }
-        } else {
-            /* we've made up our mind; just scan to end of line */
-        }
-        cp++;
-    }
-
-    return numTags;
-}
-
-/*
- * Parse the tags out of the file.
- */
-static int parseMapLines(EventTagMap* map)
-{
-    int tagNum, lineStart, lineNum;
-    char* cp = (char*) map->mapAddr;
-    char* endp = cp + map->mapLen;
-
-    /* insist on EOL at EOF; simplifies parsing and null-termination */
-    if (*(endp - 1) != '\n') {
-        fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
-        errno = EINVAL;
-        return -1;
-    }
-
-    tagNum = 0;
-    lineStart = 1;
-    lineNum = 1;
-    while (cp < endp) {
-        if (*cp == '\n') {
-            lineStart = 1;
-            lineNum++;
-        } else if (lineStart) {
-            if (*cp == '#') {
-                /* comment; just scan to end */
-                lineStart = 0;
-            } else if (isdigit(*cp)) {
-                /* looks like a tag; scan it out */
-                if (tagNum >= map->numTags) {
-                    fprintf(stderr,
-                        "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
-                    errno = EMFILE;
-                    return -1;
-                }
-                if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) {
-                    return -1;
-                }
-                tagNum++;
-                lineNum++;      // we eat the '\n'
-                /* leave lineStart==1 */
-            } else if (isspace(*cp)) {
-                /* looks like leading whitespace; keep scanning */
-            } else {
-                fprintf(stderr,
-                    "%s: unexpected chars (0x%02x) in tag number on line %d\n",
-                    OUT_TAG, *cp, lineNum);
-                errno = EINVAL;
-                return -1;
-            }
-        } else {
-            /* this is a blank or comment line */
-        }
-        cp++;
-    }
-
-    if (tagNum != map->numTags) {
-        fprintf(stderr, "%s: parsed %d tags, expected %d\n",
-            OUT_TAG, tagNum, map->numTags);
-        errno = EINVAL;
-        return -1;
-    }
-
-    return 0;
-}
-
-/*
- * Scan one tag line.
- *
- * "*pData" should be pointing to the first digit in the tag number.  On
- * successful return, it will be pointing to the last character in the
- * tag line (i.e. the character before the start of the next line).
- *
- * Returns 0 on success, nonzero on failure.
- */
-static int scanTagLine(char** pData, EventTag* tag, int lineNum)
-{
-    char* cp;
-
     unsigned long val = strtoul(*pData, &cp, 10);
     if (cp == *pData) {
-        fprintf(stderr, "%s: malformed tag number on line %d\n", OUT_TAG, lineNum);
+        fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
         errno = EINVAL;
         return -1;
     }
 
-    tag->tagIndex = val;
-    if (tag->tagIndex != val) {
-        fprintf(stderr, "%s: tag number too large on line %d\n", OUT_TAG, lineNum);
+    uint32_t tagIndex = val;
+    if (tagIndex != val) {
+        fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
         errno = ERANGE;
         return -1;
     }
@@ -388,76 +132,192 @@
     }
 
     if (*cp == '\n') {
-        fprintf(stderr, "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
+        fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
         errno = EINVAL;
         return -1;
     }
 
-    tag->tagStr = cp;
-
-    /* Determine whether "c" is a valid tag char. */
-    while (isalnum(*++cp) || (*cp == '_')) {
-    }
-    tag->tagLen = cp - tag->tagStr;
+    const char* tag = cp;
+    // Determine whether "c" is a valid tag char.
+    while (isalnum(*++cp) || (*cp == '_')) { }
+    size_t tagLen = cp - tag;
 
     if (!isspace(*cp)) {
-        fprintf(stderr, "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
+        fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
         errno = EINVAL;
         return -1;
     }
 
     while (isspace(*cp) && (*cp != '\n')) ++cp;
+    const char* fmt = NULL;
+    size_t fmtLen = 0;
     if (*cp != '#') {
-        tag->fmtStr = cp;
+        fmt = cp;
         while ((*cp != '\n') && (*cp != '#')) ++cp;
-        while ((cp > tag->fmtStr) && isspace(*(cp - 1))) --cp;
-        tag->fmtLen = cp - tag->fmtStr;
+        while ((cp > fmt) && isspace(*(cp - 1))) --cp;
+        fmtLen = cp - fmt;
     }
 
     while (*cp != '\n') ++cp;
+#ifdef DEBUG
+    fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+#endif
     *pData = cp;
 
-    return 0;
+    if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+            MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
+        return 0;
+    }
+    errno = EMLINK;
+    return -1;
 }
 
-/*
- * Compare two EventTags.
- */
-static int compareEventTags(const void* v1, const void* v2)
-{
-    const EventTag* tag1 = (const EventTag*) v1;
-    const EventTag* tag2 = (const EventTag*) v2;
+// Parse the tags out of the file.
+static int parseMapLines(EventTagMap* map) {
+    char* cp = static_cast<char*>(map->mapAddr);
+    size_t len = map->mapLen;
+    char* endp = cp + len;
 
-    return tag1->tagIndex - tag2->tagIndex;
-}
+    // insist on EOL at EOF; simplifies parsing and null-termination
+    if (!len || (*(endp - 1) != '\n')) {
+#ifdef DEBUG
+        fprintf(stderr, OUT_TAG ": map file missing EOL on last line\n");
+#endif
+        errno = EINVAL;
+        return -1;
+    }
 
-/*
- * Sort the EventTag array so we can do fast lookups by tag index.  After
- * the sort we do a quick check for duplicate tag indices.
- *
- * Returns 0 on success.
- */
-static int sortTags(EventTagMap* map)
-{
-    int i;
-
-    qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
-
-    for (i = 1; i < map->numTags; i++) {
-        if (map->tagArray[i].tagIndex == map->tagArray[i - 1].tagIndex) {
-            fprintf(stderr,
-                "%s: duplicate tag entries (%" PRIu32 ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
-                OUT_TAG,
-                map->tagArray[i].tagIndex,
-                (int)map->tagArray[i].tagLen, map->tagArray[i].tagStr,
-                (int)map->tagArray[i].fmtLen, map->tagArray[i].fmtStr,
-                map->tagArray[i - 1].tagIndex,
-                (int)map->tagArray[i - 1].tagLen, map->tagArray[i - 1].fmtStr,
-                (int)map->tagArray[i - 1].fmtLen, map->tagArray[i - 1].fmtStr);
-            errno = EMLINK;
-            return -1;
+    bool lineStart = true;
+    int lineNum = 1;
+    while (cp < endp) {
+        if (*cp == '\n') {
+            lineStart = true;
+            lineNum++;
+        } else if (lineStart) {
+            if (*cp == '#') {
+                // comment; just scan to end
+                lineStart = false;
+            } else if (isdigit(*cp)) {
+                // looks like a tag; scan it out
+                if (scanTagLine(map, &cp, lineNum) != 0) {
+                    return -1;
+                }
+                lineNum++;      // we eat the '\n'
+                // leave lineStart==true
+            } else if (isspace(*cp)) {
+                // looks like leading whitespace; keep scanning
+            } else {
+                fprintf(stderr,
+                        OUT_TAG ": unexpected chars (0x%02x) in tag number on line %d\n",
+                        *cp, lineNum);
+                errno = EINVAL;
+                return -1;
+            }
+        } else {
+            // this is a blank or comment line
         }
+        cp++;
     }
 
     return 0;
 }
+
+// Open the map file and allocate a structure to manage it.
+//
+// We create a private mapping because we want to terminate the log tag
+// strings with '\0'.
+LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
+    int save_errno;
+
+    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
+    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        save_errno = errno;
+        fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
+                tagfile, strerror(save_errno));
+        errno = save_errno;
+        return NULL;
+    }
+    off_t end = lseek(fd, 0L, SEEK_END);
+    save_errno = errno;
+    (void)lseek(fd, 0L, SEEK_SET);
+    if (end < 0) {
+        fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
+                tagfile, strerror(save_errno));
+        close(fd);
+        errno = save_errno;
+        return NULL;
+    }
+
+    EventTagMap* newTagMap = new EventTagMap;
+    if (newTagMap == NULL) {
+        save_errno = errno;
+        close(fd);
+        errno = save_errno;
+        return NULL;
+    }
+
+    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE,
+                              MAP_PRIVATE, fd, 0);
+    save_errno = errno;
+    close(fd);
+    fd = -1;
+    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
+        fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
+                tagfile, strerror(save_errno));
+        delete newTagMap;
+        errno = save_errno;
+        return NULL;
+    }
+
+    newTagMap->mapLen = end;
+
+    if (parseMapLines(newTagMap) != 0) {
+        delete newTagMap;
+        return NULL;
+    }
+
+    return newTagMap;
+}
+
+// Close the map.
+LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
+    if (map) delete map;
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
+                                                         size_t *len,
+                                                         unsigned int tag) {
+    if (len) *len = 0;
+    const TagFmt* str = map->find(tag);
+    if (!str) return NULL;
+    if (len) *len = str->first.length();
+    return str->first.data();
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
+        const EventTagMap* map, size_t *len, unsigned int tag) {
+    if (len) *len = 0;
+    const TagFmt* str = map->find(tag);
+    if (!str) return NULL;
+    if (len) *len = str->second.length();
+    return str->second.data();
+}
+
+// This function is deprecated and replaced with android_lookupEventTag_len
+// since it will cause the map to change from Shared and backed by a file,
+// to Private Dirty and backed up by swap, albeit highly compressible. By
+// deprecating this function everywhere, we save 100s of MB of memory space.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
+                                                     unsigned int tag) {
+    size_t len;
+    const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+
+    if (!tagStr) return tagStr;
+    char* cp = const_cast<char*>(tagStr);
+    cp += len;
+    if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
+    return tagStr;
+}