Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 06d8b7b8 authored by Adam Lesinski's avatar Adam Lesinski Committed by Android Git Automerger
Browse files

am cd6f00c3: Merge "Implement back-tracking when searching for attributes in...

am cd6f00c3: Merge "Implement back-tracking when searching for attributes in XML or resource bag" into lmp-mr1-dev

* commit 'cd6f00c3':
  Implement back-tracking when searching for attributes in XML or resource bag
parents 58b9ee28 cd6f00c3
Loading
Loading
Loading
Loading
+73 −62
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@

#include <androidfw/Asset.h>
#include <androidfw/AssetManager.h>
#include <androidfw/AttributeFinder.h>
#include <androidfw/ResourceTypes.h>

#include <private/android_filesystem_config.h> // for AID_SYSTEM
@@ -999,6 +1000,30 @@ static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
    theme->dumpToLog();
}

class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, jsize> {
public:
    XmlAttributeFinder(const ResXMLParser* parser)
        : BackTrackingAttributeFinder(0, parser != NULL ? parser->getAttributeCount() : 0)
        , mParser(parser) {}

    inline uint32_t getAttribute(jsize index) const {
        return mParser->getAttributeNameResID(index);
    }

private:
    const ResXMLParser* mParser;
};

class BagAttributeFinder : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
public:
    BagAttributeFinder(const ResTable::bag_entry* start, const ResTable::bag_entry* end)
        : BackTrackingAttributeFinder(start, end) {}

    inline uint32_t getAttribute(const ResTable::bag_entry* entry) const {
        return entry->map.name.ident;
    }
};

static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
                                                          jlong themeToken,
                                                          jint defStyleAttr,
@@ -1074,13 +1099,13 @@ static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject c
    res.lock();

    // Retrieve the default style bag, if requested.
    const ResTable::bag_entry* defStyleEnt = NULL;
    const ResTable::bag_entry* defStyleStart = NULL;
    uint32_t defStyleTypeSetFlags = 0;
    ssize_t bagOff = defStyleRes != 0
            ? res.getBagLocked(defStyleRes, &defStyleEnt, &defStyleTypeSetFlags) : -1;
            ? res.getBagLocked(defStyleRes, &defStyleStart, &defStyleTypeSetFlags) : -1;
    defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
    const ResTable::bag_entry* endDefStyleEnt = defStyleEnt +
        (bagOff >= 0 ? bagOff : 0);;
    const ResTable::bag_entry* const defStyleEnd = defStyleStart + (bagOff >= 0 ? bagOff : 0);
    BagAttributeFinder defStyleAttrFinder(defStyleStart, defStyleEnd);

    // Now iterate through all of the attributes that the client has requested,
    // filling in each with whatever data we can find.
@@ -1108,20 +1133,15 @@ static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject c
                    value.dataType, value.data));
        }

        // Skip through the default style values until the end or the next possible match.
        while (defStyleEnt < endDefStyleEnt && curIdent > defStyleEnt->map.name.ident) {
            defStyleEnt++;
        }
        // Retrieve the current default style attribute if it matches, and step to next.
        if (defStyleEnt < endDefStyleEnt && curIdent == defStyleEnt->map.name.ident) {
        if (value.dataType == Res_value::TYPE_NULL) {
                block = defStyleEnt->stringBlock;
            const ResTable::bag_entry* const defStyleEntry = defStyleAttrFinder.find(curIdent);
            if (defStyleEntry != defStyleEnd) {
                block = defStyleEntry->stringBlock;
                typeSetFlags = defStyleTypeSetFlags;
                value = defStyleEnt->map.value;
                value = defStyleEntry->map.value;
                DEBUG_STYLES(ALOGI("-> From def style: type=0x%x, data=0x%08x",
                        value.dataType, value.data));
            }
            defStyleEnt++;
        }

        uint32_t resid = 0;
@@ -1284,28 +1304,26 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
    res.lock();

    // Retrieve the default style bag, if requested.
    const ResTable::bag_entry* defStyleEnt = NULL;
    const ResTable::bag_entry* defStyleAttrStart = NULL;
    uint32_t defStyleTypeSetFlags = 0;
    ssize_t bagOff = defStyleRes != 0
            ? res.getBagLocked(defStyleRes, &defStyleEnt, &defStyleTypeSetFlags) : -1;
            ? res.getBagLocked(defStyleRes, &defStyleAttrStart, &defStyleTypeSetFlags) : -1;
    defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
    const ResTable::bag_entry* endDefStyleEnt = defStyleEnt +
        (bagOff >= 0 ? bagOff : 0);
    const ResTable::bag_entry* const defStyleAttrEnd = defStyleAttrStart + (bagOff >= 0 ? bagOff : 0);
    BagAttributeFinder defStyleAttrFinder(defStyleAttrStart, defStyleAttrEnd);

    // Retrieve the style class bag, if requested.
    const ResTable::bag_entry* styleEnt = NULL;
    const ResTable::bag_entry* styleAttrStart = NULL;
    uint32_t styleTypeSetFlags = 0;
    bagOff = style != 0 ? res.getBagLocked(style, &styleEnt, &styleTypeSetFlags) : -1;
    bagOff = style != 0 ? res.getBagLocked(style, &styleAttrStart, &styleTypeSetFlags) : -1;
    styleTypeSetFlags |= styleBagTypeSetFlags;
    const ResTable::bag_entry* endStyleEnt = styleEnt +
        (bagOff >= 0 ? bagOff : 0);
    const ResTable::bag_entry* const styleAttrEnd = styleAttrStart + (bagOff >= 0 ? bagOff : 0);
    BagAttributeFinder styleAttrFinder(styleAttrStart, styleAttrEnd);

    // Retrieve the XML attributes, if requested.
    const jsize NX = xmlParser ? xmlParser->getAttributeCount() : 0;
    jsize ix=0;
    uint32_t curXmlAttr = xmlParser ? xmlParser->getAttributeNameResID(ix) : 0;

    static const ssize_t kXmlBlock = 0x10000000;
    XmlAttributeFinder xmlAttrFinder(xmlParser);
    const jsize xmlAttrEnd = xmlParser != NULL ? xmlParser->getAttributeCount() : 0;

    // Now iterate through all of the attributes that the client has requested,
    // filling in each with whatever data we can find.
@@ -1324,51 +1342,40 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
        typeSetFlags = 0;
        config.density = 0;

        // Skip through XML attributes until the end or the next possible match.
        while (ix < NX && curIdent > curXmlAttr) {
            ix++;
            curXmlAttr = xmlParser->getAttributeNameResID(ix);
        }
        // Retrieve the current XML attribute if it matches, and step to next.
        if (ix < NX && curIdent == curXmlAttr) {
        // Walk through the xml attributes looking for the requested attribute.
        const jsize xmlAttrIdx = xmlAttrFinder.find(curIdent);
        if (xmlAttrIdx != xmlAttrEnd) {
            // We found the attribute we were looking for.
            block = kXmlBlock;
            xmlParser->getAttributeValue(ix, &value);
            ix++;
            curXmlAttr = xmlParser->getAttributeNameResID(ix);
            xmlParser->getAttributeValue(xmlAttrIdx, &value);
            DEBUG_STYLES(ALOGI("-> From XML: type=0x%x, data=0x%08x",
                    value.dataType, value.data));
        }

        // Skip through the style values until the end or the next possible match.
        while (styleEnt < endStyleEnt && curIdent > styleEnt->map.name.ident) {
            styleEnt++;
        }
        // Retrieve the current style attribute if it matches, and step to next.
        if (styleEnt < endStyleEnt && curIdent == styleEnt->map.name.ident) {
        if (value.dataType == Res_value::TYPE_NULL) {
                block = styleEnt->stringBlock;
            // Walk through the style class values looking for the requested attribute.
            const ResTable::bag_entry* const styleAttrEntry = styleAttrFinder.find(curIdent);
            if (styleAttrEntry != styleAttrEnd) {
                // We found the attribute we were looking for.
                block = styleAttrEntry->stringBlock;
                typeSetFlags = styleTypeSetFlags;
                value = styleEnt->map.value;
                value = styleAttrEntry->map.value;
                DEBUG_STYLES(ALOGI("-> From style: type=0x%x, data=0x%08x",
                        value.dataType, value.data));
            }
            styleEnt++;
        }

        // Skip through the default style values until the end or the next possible match.
        while (defStyleEnt < endDefStyleEnt && curIdent > defStyleEnt->map.name.ident) {
            defStyleEnt++;
        }
        // Retrieve the current default style attribute if it matches, and step to next.
        if (defStyleEnt < endDefStyleEnt && curIdent == defStyleEnt->map.name.ident) {
        if (value.dataType == Res_value::TYPE_NULL) {
                block = defStyleEnt->stringBlock;
                typeSetFlags = defStyleTypeSetFlags;
                value = defStyleEnt->map.value;
            // Walk through the default style values looking for the requested attribute.
            const ResTable::bag_entry* const defStyleAttrEntry = defStyleAttrFinder.find(curIdent);
            if (defStyleAttrEntry != defStyleAttrEnd) {
                // We found the attribute we were looking for.
                block = defStyleAttrEntry->stringBlock;
                typeSetFlags = styleTypeSetFlags;
                value = defStyleAttrEntry->map.value;
                DEBUG_STYLES(ALOGI("-> From def style: type=0x%x, data=0x%08x",
                        value.dataType, value.data));
            }
            defStyleEnt++;
        }

        uint32_t resid = 0;
@@ -1376,7 +1383,9 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
            // Take care of resolving the found resource to its final value.
            ssize_t newBlock = theme->resolveAttributeReference(&value, block,
                    &resid, &typeSetFlags, &config);
            if (newBlock >= 0) block = newBlock;
            if (newBlock >= 0) {
                block = newBlock;
            }
            DEBUG_STYLES(ALOGI("-> Resolved attr: type=0x%x, data=0x%08x",
                    value.dataType, value.data));
        } else {
@@ -1394,7 +1403,9 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
                    return JNI_FALSE;
                }
#endif
                if (newBlock >= 0) block = newBlock;
                if (newBlock >= 0) {
                    block = newBlock;
                }
                DEBUG_STYLES(ALOGI("-> Resolved theme: type=0x%x, data=0x%08x",
                        value.dataType, value.data));
            }
@@ -1414,8 +1425,8 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
        // Write the final value back to Java.
        dest[STYLE_TYPE] = value.dataType;
        dest[STYLE_DATA] = value.data;
        dest[STYLE_ASSET_COOKIE] =
            block != kXmlBlock ? reinterpret_cast<jint>(res.getTableCookie(block)) : (jint)-1;
        dest[STYLE_ASSET_COOKIE] = block != kXmlBlock ?
            static_cast<jint>(res.getTableCookie(block)) : -1;
        dest[STYLE_RESOURCE_ID] = resid;
        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
        dest[STYLE_DENSITY] = config.density;
+201 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef H_ATTRIBUTE_FINDER
#define H_ATTRIBUTE_FINDER

#include <stdint.h>
#include <utils/KeyedVector.h>

namespace android {

static inline uint32_t getPackage(uint32_t attr) {
    return attr >> 24;
}

/**
 * A helper class to search linearly for the requested
 * attribute, maintaining it's position and optimizing for
 * the case that subsequent searches will involve an attribute with
 * a higher attribute ID.
 *
 * In the case that a subsequent attribute has a different package ID,
 * its resource ID may not be larger than the preceding search, so
 * back tracking is supported for this case. This
 * back tracking requirement is mainly for shared library
 * resources, whose package IDs get assigned at runtime
 * and thus attributes from a shared library may
 * be out of order.
 *
 * We make two assumptions about the order of attributes:
 * 1) The input has the same sorting rules applied to it as
 *    the attribute data contained by this class.
 * 2) Attributes are grouped by package ID.
 * 3) Among attributes with the same package ID, the attributes are
 *    sorted by increasing resource ID.
 *
 * Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003
 *
 * The total order of attributes (including package ID) can not be linear
 * as shared libraries get assigned dynamic package IDs at runtime, which
 * may break the sort order established at build time.
 */
template <typename Derived, typename Iterator>
class BackTrackingAttributeFinder {
public:
    BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);

    Iterator find(uint32_t attr);

private:
    void jumpToClosestAttribute(uint32_t packageId);
    void markCurrentPackageId(uint32_t packageId);

    Iterator mBegin;
    Iterator mEnd;
    Iterator mCurrent;
    Iterator mLargest;
    uint32_t mLastPackageId;
    uint32_t mCurrentAttr;

    // Package Offsets (best-case, fast look-up).
    Iterator mFrameworkStart;
    Iterator mAppStart;

    // Worst case, we have shared-library resources.
    KeyedVector<uint32_t, Iterator> mPackageOffsets;
};

template <typename Derived, typename Iterator> inline
BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end)
    : mBegin(begin)
    , mEnd(end)
    , mCurrent(begin)
    , mLargest(begin)
    , mLastPackageId(0)
    , mCurrentAttr(0)
    , mFrameworkStart(end)
    , mAppStart(end) {
}

template <typename Derived, typename Iterator>
void BackTrackingAttributeFinder<Derived, Iterator>::jumpToClosestAttribute(const uint32_t packageId) {
    switch (packageId) {
        case 0x01:
            mCurrent = mFrameworkStart;
            break;
        case 0x7f:
            mCurrent = mAppStart;
            break;
        default: {
            ssize_t idx = mPackageOffsets.indexOfKey(packageId);
            if (idx >= 0) {
                // We have seen this package ID before, so jump to the first
                // attribute with this package ID.
                mCurrent = mPackageOffsets[idx];
            } else {
                mCurrent = mEnd;
            }
            break;
        }
    }

    // We have never seen this package ID yet, so jump to the
    // latest/largest index we have processed so far.
    if (mCurrent == mEnd) {
        mCurrent = mLargest;
    }

    if (mCurrent != mEnd) {
        mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
    }
}

template <typename Derived, typename Iterator>
void BackTrackingAttributeFinder<Derived, Iterator>::markCurrentPackageId(const uint32_t packageId) {
    switch (packageId) {
        case 0x01:
            mFrameworkStart = mCurrent;
            break;
        case 0x7f:
            mAppStart = mCurrent;
            break;
        default:
            mPackageOffsets.add(packageId, mCurrent);
            break;
    }
}

template <typename Derived, typename Iterator>
Iterator BackTrackingAttributeFinder<Derived, Iterator>::find(uint32_t attr) {
    if (!(mBegin < mEnd)) {
        return mEnd;
    }

    if (mCurrentAttr == 0) {
        // One-time initialization.
        mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mBegin);
        mLastPackageId = getPackage(mCurrentAttr);
        markCurrentPackageId(mLastPackageId);
    }

    // Looking for the needle (attribute we're looking for)
    // in the haystack (the attributes we're searching through)
    const uint32_t needlePackageId = getPackage(attr);
    if (mLastPackageId != needlePackageId) {
        jumpToClosestAttribute(needlePackageId);
        mLastPackageId = needlePackageId;
    }

    // Walk through the xml attributes looking for the requested attribute.
    while (mCurrent != mEnd) {
        const uint32_t haystackPackageId = getPackage(mCurrentAttr);
        if (needlePackageId == haystackPackageId && attr < mCurrentAttr) {
            // The attribute we are looking was not found.
            break;
        }
        const uint32_t prevAttr = mCurrentAttr;

        // Move to the next attribute in the XML.
        ++mCurrent;
        if (mCurrent != mEnd) {
            mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
            const uint32_t newHaystackPackageId = getPackage(mCurrentAttr);
            if (haystackPackageId != newHaystackPackageId) {
                // We've moved to the next group of attributes
                // with a new package ID, so we should record
                // the offset of this new package ID.
                markCurrentPackageId(newHaystackPackageId);
            }
        }

        if (mCurrent > mLargest) {
            // We've moved past the latest attribute we've
            // seen.
            mLargest = mCurrent;
        }

        if (attr == prevAttr) {
            // We found the attribute we were looking for.
            return mCurrent - 1;
        }
    }
    return mEnd;
}

} // namespace android

#endif // H_ATTRIBUTE_FINDER
+7 −3
Original line number Diff line number Diff line
@@ -1185,7 +1185,11 @@ uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
{
    int32_t id = getAttributeNameID(idx);
    if (id >= 0 && (size_t)id < mTree.mNumResIds) {
        return dtohl(mTree.mResIds[id]);
        uint32_t resId = dtohl(mTree.mResIds[id]);
        if (mTree.mDynamicRefTable != NULL) {
            mTree.mDynamicRefTable->lookupResourceId(&resId);
        }
        return resId;
    }
    return 0;
}
@@ -5977,11 +5981,11 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
    // Do a proper lookup.
    uint8_t translatedId = mLookupTable[packageId];
    if (translatedId == 0) {
        ALOGE("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
        ALOGV("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
                (uint8_t)mAssignedPackageId, (uint8_t)packageId);
        for (size_t i = 0; i < 256; i++) {
            if (mLookupTable[i] != 0) {
                ALOGE("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
                ALOGV("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
            }
        }
        return UNKNOWN_ERROR;
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
# ==========================================================
LOCAL_PATH:= $(call my-dir)
testFiles := \
    AttributeFinder_test.cpp \
    ByteBucketArray_test.cpp \
    Config_test.cpp \
    ConfigLocale_test.cpp \
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <androidfw/AttributeFinder.h>

#include <gtest/gtest.h>

using android::BackTrackingAttributeFinder;

class MockAttributeFinder : public BackTrackingAttributeFinder<MockAttributeFinder, int> {
public:
    MockAttributeFinder(const uint32_t* attrs, int len)
        : BackTrackingAttributeFinder(0, len) {
        mAttrs = new uint32_t[len];
        memcpy(mAttrs, attrs, sizeof(*attrs) * len);
    }

    ~MockAttributeFinder() {
        delete mAttrs;
    }

    inline uint32_t getAttribute(const int index) const {
        return mAttrs[index];
    }

private:
    uint32_t* mAttrs;
};

static const uint32_t sortedAttributes[] = {
        0x01010000, 0x01010001, 0x01010002, 0x01010004,
        0x02010001, 0x02010010, 0x7f010001
};

static const uint32_t packageUnsortedAttributes[] = {
        0x02010001, 0x02010010, 0x01010000, 0x01010001,
        0x01010002, 0x01010004, 0x7f010001
};

TEST(AttributeFinderTest, IteratesSequentially) {
    const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
    MockAttributeFinder finder(sortedAttributes, end);

    EXPECT_EQ(0, finder.find(0x01010000));
    EXPECT_EQ(1, finder.find(0x01010001));
    EXPECT_EQ(2, finder.find(0x01010002));
    EXPECT_EQ(3, finder.find(0x01010004));
    EXPECT_EQ(4, finder.find(0x02010001));
    EXPECT_EQ(5, finder.find(0x02010010));
    EXPECT_EQ(6, finder.find(0x7f010001));
    EXPECT_EQ(end, finder.find(0x7f010002));
}

TEST(AttributeFinderTest, PackagesAreOutOfOrder) {
    const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
    MockAttributeFinder finder(sortedAttributes, end);

    EXPECT_EQ(6, finder.find(0x7f010001));
    EXPECT_EQ(end, finder.find(0x7f010002));
    EXPECT_EQ(4, finder.find(0x02010001));
    EXPECT_EQ(5, finder.find(0x02010010));
    EXPECT_EQ(0, finder.find(0x01010000));
    EXPECT_EQ(1, finder.find(0x01010001));
    EXPECT_EQ(2, finder.find(0x01010002));
    EXPECT_EQ(3, finder.find(0x01010004));
}

TEST(AttributeFinderTest, SomeAttributesAreNotFound) {
    const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
    MockAttributeFinder finder(sortedAttributes, end);

    EXPECT_EQ(0, finder.find(0x01010000));
    EXPECT_EQ(1, finder.find(0x01010001));
    EXPECT_EQ(2, finder.find(0x01010002));
    EXPECT_EQ(end, finder.find(0x01010003));
    EXPECT_EQ(3, finder.find(0x01010004));
    EXPECT_EQ(end, finder.find(0x01010005));
    EXPECT_EQ(end, finder.find(0x01010006));
    EXPECT_EQ(4, finder.find(0x02010001));
    EXPECT_EQ(end, finder.find(0x02010002));
}

TEST(AttributeFinderTest, FindAttributesInPackageUnsortedAttributeList) {
    const int end = sizeof(packageUnsortedAttributes) / sizeof(*packageUnsortedAttributes);
    MockAttributeFinder finder(packageUnsortedAttributes, end);

    EXPECT_EQ(2, finder.find(0x01010000));
    EXPECT_EQ(3, finder.find(0x01010001));
    EXPECT_EQ(4, finder.find(0x01010002));
    EXPECT_EQ(end, finder.find(0x01010003));
    EXPECT_EQ(5, finder.find(0x01010004));
    EXPECT_EQ(end, finder.find(0x01010005));
    EXPECT_EQ(end, finder.find(0x01010006));
    EXPECT_EQ(0, finder.find(0x02010001));
    EXPECT_EQ(end, finder.find(0x02010002));
    EXPECT_EQ(1, finder.find(0x02010010));
    EXPECT_EQ(6, finder.find(0x7f010001));
}