]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
We like stability.
[cydia.git] / Cydia.mm
index 172b672eeb3cb4ab23d3eb594bb039ad13fa19f4..9962b6554821316891ba652f4f9d8ad911475afc 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
 #include <GraphicsServices/GraphicsServices.h>
 #include <Foundation/Foundation.h>
 
+#if 0
+#define DEPLOYMENT_TARGET_MACOSX 1
+#define CF_BUILDING_CF 1
+#include <CoreFoundation/CFInternal.h>
+#endif
+
 #include <CoreFoundation/CFPriv.h>
 #include <CoreFoundation/CFUniChar.h>
 
@@ -81,6 +87,7 @@
 #include <apt-pkg/sourcelist.h>
 #include <apt-pkg/sptr.h>
 #include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile.h>
 
 #include <apr-1/apr_pools.h>
 
@@ -220,12 +227,18 @@ class _H {
         Clear_();
     }
 
+    _finline operator Type_ *() const {
+        return value_;
+    }
+
     _finline This_ &operator =(Type_ *value) {
         if (value_ != value) {
-            Clear_();
+            Type_ *old(value_);
             value_ = value;
             Retain_();
-        } return this;
+            if (old != nil)
+                [old release];
+        } return *this;
     }
 };
 /* }}} */
@@ -391,8 +404,11 @@ extern NSString * const kCAFilterNearest;
 #define lprintf(args...) fprintf(stderr, args)
 
 #define ForRelease 0
+#define TraceLogging (1 && !ForRelease)
+#define HistogramInsertionSort (0 && !ForRelease)
+#define ProfileTimes (0 && !ForRelease)
 #define ForSaurik (0 && !ForRelease)
-#define LogBrowser (0 && !ForRelease)
+#define LogBrowser (1 && !ForRelease)
 #define TrackResize (0 && !ForRelease)
 #define ManualRefresh (1 && !ForRelease)
 #define ShowInternals (0 && !ForRelease)
@@ -400,9 +416,12 @@ extern NSString * const kCAFilterNearest;
 #define RecycleWebViews 0
 #define AlwaysReload (1 && !ForRelease)
 
-#if ForRelease
+#if !TraceLogging
 #undef _trace
 #define _trace(args...)
+#endif
+
+#if !ProfileTimes
 #undef _profile
 #define _profile(name) {
 #undef _end
@@ -411,9 +430,11 @@ extern NSString * const kCAFilterNearest;
 #endif
 
 /* Radix Sort {{{ */
+typedef uint32_t (*SKRadixFunction)(id, void *);
+
 @interface NSMutableArray (Radix)
 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
-- (void) radixSortUsingFunction:(uint32_t (*)(id, void *))function withArgument:(void *)argument;
+- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument;
 @end
 
 struct RadixItem_ {
@@ -473,11 +494,22 @@ static void RadixSort_(NSMutableArray *self, size_t count, struct RadixItem_ *sw
 @implementation NSMutableArray (Radix)
 
 - (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
+    size_t count([self count]);
+    if (count == 0)
+        return;
+
+#if 0
     NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
     [invocation setSelector:selector];
     [invocation setArgument:&object atIndex:2];
+#else
+    /* XXX: this is an unsafe optimization of doomy hell */
+    Method method(class_getInstanceMethod([[self objectAtIndex:0] class], selector));
+    _assert(method != NULL);
+    uint32_t (*imp)(id, SEL, id) = reinterpret_cast<uint32_t (*)(id, SEL, id)>(method_getImplementation(method));
+    _assert(imp != NULL);
+#endif
 
-    size_t count([self count]);
     struct RadixItem_ *swap(new RadixItem_[count * 2]);
 
     for (size_t i(0); i != count; ++i) {
@@ -485,16 +517,20 @@ static void RadixSort_(NSMutableArray *self, size_t count, struct RadixItem_ *sw
         item.index = i;
 
         id object([self objectAtIndex:i]);
-        [invocation setTarget:object];
 
+#if 0
+        [invocation setTarget:object];
         [invocation invoke];
         [invocation getReturnValue:&item.key];
+#else
+        item.key = imp(object, selector, object);
+#endif
     }
 
     RadixSort_(self, count, swap);
 }
 
-- (void) radixSortUsingFunction:(uint32_t (*)(id, void *))function withArgument:(void *)argument {
+- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument {
     size_t count([self count]);
     struct RadixItem_ *swap(new RadixItem_[count * 2]);
 
@@ -510,6 +546,77 @@ static void RadixSort_(NSMutableArray *self, size_t count, struct RadixItem_ *sw
 }
 
 @end
+/* }}} */
+/* Insertion Sort {{{ */
+
+CFIndex SKBSearch_(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) {
+    const char *ptr = (const char *)list;
+    while (0 < count) {
+        CFIndex half = count / 2;
+        const char *probe = ptr + elementSize * half;
+        CFComparisonResult cr = comparator(element, probe, context);
+       if (0 == cr) return (probe - (const char *)list) / elementSize;
+        ptr = (cr < 0) ? ptr : probe + elementSize;
+        count = (cr < 0) ? half : (half + (count & 1) - 1);
+    }
+    return (ptr - (const char *)list) / elementSize;
+}
+
+CFIndex CFBSearch_(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) {
+    const char *ptr = (const char *)list;
+    while (0 < count) {
+        CFIndex half = count / 2;
+        const char *probe = ptr + elementSize * half;
+        CFComparisonResult cr = comparator(element, probe, context);
+       if (0 == cr) return (probe - (const char *)list) / elementSize;
+        ptr = (cr < 0) ? ptr : probe + elementSize;
+        count = (cr < 0) ? half : (half + (count & 1) - 1);
+    }
+    return (ptr - (const char *)list) / elementSize;
+}
+
+void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunction comparator, void *context) {
+    if (range.length == 0)
+        return;
+    const void **values(new const void *[range.length]);
+    CFArrayGetValues(array, range, values);
+
+#if HistogramInsertionSort
+    uint32_t total(0), *offsets(new uint32_t[range.length]);
+#endif
+
+    for (CFIndex index(1); index != range.length; ++index) {
+        const void *value(values[index]);
+        //CFIndex correct(SKBSearch_(&value, sizeof(const void *), values, index, comparator, context));
+        CFIndex correct(index);
+        while (comparator(value, values[correct - 1], context) == kCFCompareLessThan)
+            if (--correct == 0)
+                break;
+        if (correct != index) {
+            size_t offset(index - correct);
+#if HistogramInsertionSort
+            total += offset;
+            ++offsets[offset];
+            if (offset > 10)
+                NSLog(@"Heavy Insertion Displacement: %u = %@", offset, value);
+#endif
+            memmove(values + correct + 1, values + correct, sizeof(const void *) * offset);
+            values[correct] = value;
+        }
+    }
+
+    CFArrayReplaceValues(array, range, values, range.length);
+    delete [] values;
+
+#if HistogramInsertionSort
+    for (CFIndex index(0); index != range.length; ++index)
+        if (offsets[index] != 0)
+            NSLog(@"Insertion Displacement [%u]: %u", index, offsets[index]);
+    NSLog(@"Average Insertion Displacement: %f", double(total) / range.length);
+    delete [] offsets;
+#endif
+}
+
 /* }}} */
 
 /* Apple Bug Fixes {{{ */
@@ -657,8 +764,10 @@ class CYString {
     CFStringRef cache_;
 
     _finline void clear_() {
-        if (cache_ != nil)
+        if (cache_ != NULL) {
             CFRelease(cache_);
+            cache_ = NULL;
+        }
     }
 
   public:
@@ -682,7 +791,7 @@ class CYString {
     _finline CYString() :
         data_(0),
         size_(0),
-        cache_(nil)
+        cache_(NULL)
     {
     }
 
@@ -726,12 +835,16 @@ class CYString {
         return size_ == rhs.size_ && memcmp(data_, rhs.data_, size_) == 0;
     }
 
-    operator id() {
+    operator CFStringRef() {
         if (cache_ == NULL) {
             if (size_ == 0)
                 return nil;
             cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<uint8_t *>(data_), size_, kCFStringEncodingUTF8, NO, kCFAllocatorNull);
-        } return (id) cache_;
+        } return cache_;
+    }
+
+    _finline operator id() {
+        return (NSString *) static_cast<CFStringRef>(*this);
     }
 };
 
@@ -1028,18 +1141,15 @@ NSString *SizeString(double size) {
     return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
 }
 
-NSString *StripVersion(const char *version) {
+static _finline CFStringRef CFCString(const char *value) {
+    return CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(value), strlen(value), kCFStringEncodingUTF8, NO, kCFAllocatorNull);
+}
+
+CFStringRef StripVersion(const char *version) {
     const char *colon(strchr(version, ':'));
     if (colon != NULL)
         version = colon + 1;
-    return [NSString stringWithUTF8String:version];
-}
-
-NSString *StripVersion(NSString *version) {
-    NSRange colon = [version rangeOfString:@":"];
-    if (colon.location != NSNotFound)
-        version = [version substringFromIndex:(colon.location + 1)];
-    return version;
+    return CFCString(version);
 }
 
 NSString *LocalizeSection(NSString *section) {
@@ -1075,10 +1185,6 @@ NSString *Simplify(NSString *title) {
 
     return title;
 }
-
-_finline static void Stifle(char &value) {
-    value = (value & 0xdf) ^ 0x40;
-}
 /* }}} */
 
 bool isSectionVisible(NSString *section) {
@@ -1247,6 +1353,8 @@ class Progress :
 /* }}} */
 
 /* Database Interface {{{ */
+typedef std::map< unsigned long, _H<Source> > SourceMap;
+
 @interface Database : NSObject {
     NSZone *zone_;
     apr_pool_t *pool_;
@@ -1262,7 +1370,7 @@ class Progress :
     SPtr<pkgPackageManager> manager_;
     pkgSourceList *list_;
 
-    NSMutableDictionary *sources_;
+    SourceMap sources_;
     NSMutableArray *packages_;
 
     _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
@@ -1301,7 +1409,7 @@ class Progress :
 - (void) upgrade;
 - (void) update;
 
-- (void) updateWithStatus:(Status &)status;
+- (NSString *) updateWithStatus:(Status &)status;
 
 - (void) setDelegate:(id)delegate;
 - (Source *) getSource:(pkgCache::PkgFileIterator)file;
@@ -1310,23 +1418,25 @@ class Progress :
 
 /* Source Class {{{ */
 @interface Source : NSObject {
-    NSString *description_;
-    NSString *label_;
-    NSString *origin_;
-    NSString *support_;
+    CYString description_;
+    CYString label_;
+    CYString origin_;
+    CYString support_;
 
-    NSString *uri_;
-    NSString *distribution_;
-    NSString *type_;
-    NSString *version_;
+    CYString uri_;
+    CYString distribution_;
+    CYString type_;
+    CYString version_;
+
+    NSString *host_;
 
-    NSString *defaultIcon_;
+    CYString defaultIcon_;
 
     NSDictionary *record_;
     BOOL trusted_;
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index;
+- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool;
 
 - (NSComparisonResult) compareByNameAndType:(Source *)source;
 
@@ -1353,23 +1463,27 @@ class Progress :
 
 @implementation Source
 
-#define _clear(field) \
-    if (field != nil) \
-        [field release]; \
-    field = nil;
-
 - (void) _clear {
-    _clear(uri_)
-    _clear(distribution_)
-    _clear(type_)
+    uri_.clear();
+    distribution_.clear();
+    type_.clear();
+
+    description_.clear();
+    label_.clear();
+    origin_.clear();
+    support_.clear();
+    version_.clear();
+    defaultIcon_.clear();
+
+    if (record_ != nil) {
+        [record_ release];
+        record_ = nil;
+    }
 
-    _clear(description_)
-    _clear(label_)
-    _clear(origin_)
-    _clear(support_)
-    _clear(version_)
-    _clear(defaultIcon_)
-    _clear(record_)
+    if (host_ != nil) {
+        [host_ release];
+        host_ = nil;
+    }
 }
 
 - (void) dealloc {
@@ -1389,52 +1503,59 @@ class Progress :
     return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
 }
 
-- (void) setMetaIndex:(metaIndex *)index {
+- (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
     [self _clear];
 
     trusted_ = index->IsTrusted();
 
-    uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
-    distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
-    type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
+    uri_.set(pool, index->GetURI());
+    distribution_.set(pool, index->GetDist());
+    type_.set(pool, index->GetType());
 
     debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
     if (dindex != NULL) {
-        std::ifstream release(dindex->MetaIndexFile("Release").c_str());
-        std::string line;
-        while (std::getline(release, line)) {
-            std::string::size_type colon(line.find(':'));
-            if (colon == std::string::npos)
-                continue;
-
-            std::string name(line.substr(0, colon));
-            std::string value(line.substr(colon + 1));
-            while (!value.empty() && value[0] == ' ')
-                value = value.substr(1);
-
-            if (name == "Default-Icon")
-                defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Description")
-                description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Label")
-                label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Origin")
-                origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Support")
-                support_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Version")
-                version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+        FileFd fd;
+        if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            pkgTagFile tags(&fd);
+
+            pkgTagSection section;
+            tags.Step(section);
+
+            struct {
+                const char *name_;
+                CYString *value_;
+            } names[] = {
+                {"default-icon", &defaultIcon_},
+                {"description", &description_},
+                {"label", &label_},
+                {"origin", &origin_},
+                {"support", &support_},
+                {"version", &version_},
+            };
+
+            for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
+                const char *start, *end;
+
+                if (section.Find(names[i].name_, start, end)) {
+                    CYString &value(*names[i].value_);
+                    value.set(pool, start, end - start);
+                }
+            }
         }
     }
 
     record_ = [Sources_ objectForKey:[self key]];
     if (record_ != nil)
         record_ = [record_ retain];
+
+    host_ = [[[[NSURL URLWithString:uri_] host] lowercaseString] retain];
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index {
+- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
     if ((self = [super init]) != nil) {
-        [self setMetaIndex:index];
+        [self setMetaIndex:index inPool:pool];
     } return self;
 }
 
@@ -1462,7 +1583,7 @@ class Progress :
 }
 
 - (NSString *) supportForPackage:(NSString *)package {
-    return support_ == nil ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
+    return support_.empty() ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
 }
 
 - (NSDictionary *) record {
@@ -1486,15 +1607,15 @@ class Progress :
 }
 
 - (NSString *) key {
-    return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
+    return [NSString stringWithFormat:@"%@:%@:%@", (NSString *) type_, (NSString *) uri_, (NSString *) distribution_];
 }
 
 - (NSString *) host {
-    return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
+    return host_;
 }
 
 - (NSString *) name {
-    return origin_ == nil ? [self host] : origin_;
+    return origin_.empty() ? host_ : origin_;
 }
 
 - (NSString *) description {
@@ -1502,7 +1623,7 @@ class Progress :
 }
 
 - (NSString *) label {
-    return label_ == nil ? [self host] : label_;
+    return label_.empty() ? host_ : label_;
 }
 
 - (NSString *) origin {
@@ -1557,6 +1678,7 @@ class Progress :
 /* Package Class {{{ */
 @interface Package : NSObject {
     unsigned era_;
+    apr_pool_t *pool_;
 
     pkgCache::VerIterator version_;
     pkgCache::PkgIterator iterator_;
@@ -1565,6 +1687,7 @@ class Progress :
 
     Source *source_;
     bool cached_;
+    bool parsed_;
 
     CYString section_;
     NSString *section$_;
@@ -1588,7 +1711,7 @@ class Progress :
     Address *author$_;
 
     CYString support_;
-    NSArray *tags_;
+    NSMutableArray *tags_;
     NSString *role_;
 
     NSArray *relationships_;
@@ -1603,6 +1726,7 @@ class Progress :
 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
 
 - (pkgCache::PkgIterator) iterator;
+- (void) parse;
 
 - (NSString *) section;
 - (NSString *) simpleSection;
@@ -1614,7 +1738,8 @@ class Progress :
 
 - (Address *) maintainer;
 - (size_t) size;
-- (NSString *) description;
+- (NSString *) longDescription;
+- (NSString *) shortDescription;
 - (unichar) index;
 
 - (NSMutableDictionary *) metadata;
@@ -1640,7 +1765,6 @@ class Progress :
 
 - (NSString *) id;
 - (NSString *) name;
-- (NSString *) tagline;
 - (UIImage *) icon;
 - (NSString *) homepage;
 - (NSString *) depiction;
@@ -1664,7 +1788,8 @@ class Progress :
 - (NSArray *) purposes;
 - (bool) isCommercial;
 
-- (uint32_t) compareByPrefix;
+- (CYString &) cyname;
+
 - (uint32_t) compareBySection:(NSArray *)sections;
 
 - (uint32_t) compareForChanges;
@@ -1706,27 +1831,73 @@ uint32_t PackageChangesRadix(Package *self, void *) {
     return _not(uint32_t) - value.key;
 }
 
-CFStringRef (*PackageName)(Package *self, SEL sel);
+_finline static void Stifle(uint8_t &value) {
+}
 
-CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) {
-    _profile(PackageNameCompare)
-        CFStringRef lhn, rhn;
-        CFIndex length;
+uint32_t PackagePrefixRadix(Package *self, void *context) {
+    size_t offset(reinterpret_cast<size_t>(context));
+    CYString &name([self cyname]);
 
-        _profile(PackageNameCompare$Setup)
-            lhn = PackageName(lhs, @selector(name));
-            rhn = PackageName(rhs, @selector(name));
-        _end
+    size_t size(name.size());
+    if (size == 0)
+        return 0;
+    char *text(name.data());
 
-        _profile(PackageNameCompare$Nothing)
-        _end
+    size_t zeros;
+    if (!isdigit(text[0]))
+        zeros = 0;
+    else {
+        size_t digits(1);
+        while (size != digits && isdigit(text[digits]))
+            if (++digits == 4)
+                break;
+        zeros = 4 - digits;
+    }
 
-        _profile(PackageNameCompare$Length)
-            length = CFStringGetLength(lhn);
-        _end
+    uint8_t data[4];
+
+    // 0.607997
+
+    if (offset == 0 && zeros != 0) {
+        memset(data, '0', zeros);
+        memcpy(data + zeros, text, 4 - zeros);
+    } else {
+        /* XXX: there's some danger here if you request a non-zero offset < 4 and it gets zero padded */
+        if (size <= offset - zeros)
+            return 0;
+
+        text += offset - zeros;
+        size -= offset - zeros;
+
+        if (size >= 4)
+            memcpy(data, text, 4);
+        else {
+            memcpy(data, text, size);
+            memset(data + size, 0, 4 - size);
+        }
+
+        for (size_t i(0); i != 4; ++i)
+            if (isalpha(data[i]))
+                data[i] &= 0xdf;
+    }
+
+    if (offset == 0)
+        data[0] = (data[0] & 0x3f) | "\x80\x00\xc0\x40"[data[0] >> 6];
+
+    /* XXX: ntohl may be more honest */
+    return OSSwapInt32(*reinterpret_cast<uint32_t *>(data));
+}
+
+CYString &(*PackageName)(Package *self, SEL sel);
+
+CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) {
+    _profile(PackageNameCompare)
+        CYString &lhi(PackageName(lhs, @selector(cyname)));
+        CYString &rhi(PackageName(rhs, @selector(cyname)));
+        CFStringRef lhn(lhi), rhn(rhi);
 
         _profile(PackageNameCompare$NumbersLast)
-            if (length != 0 && CFStringGetLength(rhn) != 0) {
+            if (!lhi.empty() && !rhi.empty()) {
                 UniChar lhc(CFStringGetCharacterAtIndex(lhn, 0));
                 UniChar rhc(CFStringGetCharacterAtIndex(rhn, 0));
                 bool lha(CFUniCharIsMemberOf(lhc, kCFUniCharLetterCharacterSet));
@@ -1735,14 +1906,16 @@ CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) {
             }
         _end
 
+        CFIndex length = CFStringGetLength(lhn);
+
         _profile(PackageNameCompare$Compare)
             return CFStringCompareWithOptionsAndLocale(lhn, rhn, CFRangeMake(0, length), LaxCompareFlags_, Locale_);
         _end
     _end
 }
 
-CFComparisonResult PackageNameCompare_(Package **lhs, Package **rhs, void *arg) {
-    return PackageNameCompare(*lhs, *rhs, arg);
+CFComparisonResult PackageNameCompare_(Package **lhs, Package **rhs, void *context) {
+    return PackageNameCompare(*lhs, *rhs, context);
 }
 
 struct PackageNameOrdering :
@@ -1755,6 +1928,10 @@ struct PackageNameOrdering :
 
 @implementation Package
 
+- (NSString *) description {
+    return [NSString stringWithFormat:@"<Package:%@>", static_cast<NSString *>(name_)];
+}
+
 - (void) dealloc {
     if (source_ != nil)
         [source_ release];
@@ -1795,7 +1972,7 @@ struct PackageNameOrdering :
 }
 
 + (NSArray *) _attributeKeys {
-    return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"longSection", @"maintainer", @"mode", @"name", @"purposes", @"section", @"shortSection", @"simpleSection", @"size", @"source", @"sponsor", @"support", @"tagline", @"warnings", nil];
+    return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"longDescription", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"longSection", @"maintainer", @"mode", @"name", @"purposes", @"section", @"shortDescription", @"shortSection", @"simpleSection", @"size", @"source", @"sponsor", @"support", @"warnings", nil];
 }
 
 - (NSArray *) attributeKeys {
@@ -1806,100 +1983,124 @@ struct PackageNameOrdering :
     return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
 }
 
+- (void) parse {
+    if (parsed_)
+        return;
+    parsed_ = true;
+    if (file_.end())
+        return;
+
+    _profile(Package$parse)
+        pkgRecords::Parser *parser;
+
+        _profile(Package$parse$Lookup)
+            parser = &[database_ records]->Lookup(file_);
+        _end
+
+        CYString website;
+
+        _profile(Package$parse$Find)
+            struct {
+                const char *name_;
+                CYString *value_;
+            } names[] = {
+                {"icon", &icon_},
+                {"depiction", &depiction_},
+                {"homepage", &homepage_},
+                {"website", &website},
+                {"support", &support_},
+                {"sponsor", &sponsor_},
+                {"author", &author_},
+            };
+
+            for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
+                const char *start, *end;
+
+                if (parser->Find(names[i].name_, start, end)) {
+                    CYString &value(*names[i].value_);
+                    _profile(Package$parse$Value)
+                        value.set(pool_, start, end - start);
+                    _end
+                }
+            }
+        _end
+
+        _profile(Package$parse$Tagline)
+            const char *start, *end;
+            if (parser->ShortDesc(start, end)) {
+                const char *stop(reinterpret_cast<const char *>(memchr(start, '\n', end - start)));
+                if (stop == NULL)
+                    stop = end;
+                while (stop != start && stop[-1] == '\r')
+                    --stop;
+                tagline_.set(pool_, start, stop - start);
+            }
+        _end
+
+        _profile(Package$parse$Retain)
+            if (!homepage_.empty())
+                homepage_ = website;
+            if (homepage_ == depiction_)
+                homepage_.clear();
+        _end
+    _end
+}
+
 - (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
     if ((self = [super init]) != nil) {
     _profile(Package$initWithVersion)
     @synchronized (database) {
         era_ = [database era];
+        pool_ = pool;
 
         version_ = version;
         iterator_ = version.ParentPkg();
         database_ = database;
 
         _profile(Package$initWithVersion$Latest)
-            latest_ = [StripVersion(version_.VerStr()) retain];
+            latest_ = (NSString *) StripVersion(version_.VerStr());
         _end
 
-        pkgCache::VerIterator current(iterator_.CurrentVer());
-        if (!current.end())
-            installed_ = [StripVersion(current.VerStr()) retain];
+        pkgCache::VerIterator current;
+        _profile(Package$initWithVersion$Versions)
+            current = iterator_.CurrentVer();
+            if (!current.end())
+                installed_ = (NSString *) StripVersion(current.VerStr());
 
-        if (!version_.end())
-            file_ = version_.FileList();
-        else {
-            pkgCache &cache([database_ cache]);
-            file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
-        }
+            if (!version_.end())
+                file_ = version_.FileList();
+            else {
+                pkgCache &cache([database_ cache]);
+                file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
+            }
+        _end
 
         _profile(Package$initWithVersion$Name)
-            id_.set(pool, iterator_.Name());
+            id_.set(pool_, iterator_.Name());
+            name_.set(pool, iterator_.Display());
         _end
 
-        if (!file_.end())
-            source_ = [database_ getSource:file_.File()];
-            if (source_ != nil)
-                [source_ retain];
-            cached_ = true;
-
-            _profile(Package$initWithVersion$Parse)
-                pkgRecords::Parser *parser;
-
-                _profile(Package$initWithVersion$Parse$Lookup)
-                    parser = &[database_ records]->Lookup(file_);
-                _end
-
-                CYString website;
-                CYString tag;
-
-                _profile(Package$initWithVersion$Parse$Find)
-                    struct {
-                        const char *name_;
-                        CYString *value_;
-                    } names[] = {
-                        {"name", &name_},
-                        {"icon", &icon_},
-                        {"depiction", &depiction_},
-                        {"homepage", &homepage_},
-                        {"website", &website},
-                        {"support", &support_},
-                        {"sponsor", &sponsor_},
-                        {"author", &author_},
-                        {"tag", &tag},
-                    };
-
-                    for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
-                        const char *start, *end;
-
-                        if (parser->Find(names[i].name_, start, end)) {
-                            CYString &value(*names[i].value_);
-                            _profile(Package$initWithVersion$Parse$Value)
-                                value.set(pool, start, end - start);
-                            _end
-                        }
-                    }
-                _end
-
-                _profile(Package$initWithVersion$Parse$Tagline)
-                    tagline_.set(pool, parser->ShortDesc());
-                _end
-
-                _profile(Package$initWithVersion$Parse$Retain)
-                    if (!homepage_.empty())
-                        homepage_ = website;
-                    if (homepage_ == depiction_)
-                        homepage_.clear();
-                    if (!tag.empty())
-                        tags_ = [[tag componentsSeparatedByString:@", "] retain];
-                _end
+        if (!file_.end()) {
+            _profile(Package$initWithVersion$Source)
+                source_ = [database_ getSource:file_.File()];
+                if (source_ != nil)
+                    [source_ retain];
+                cached_ = true;
             _end
+        }
 
         _profile(Package$initWithVersion$Tags)
-            if (tags_ != nil)
-                for (NSString *tag in tags_)
-                    if ([tag hasPrefix:@"role::"]) {
-                        role_ = [[tag substringFromIndex:6] retain];
-                        break;
-                    }
+            pkgCache::TagIterator tag(iterator_.TagList());
+            if (!tag.end()) {
+                tags_ = [[NSMutableArray alloc] initWithCapacity:8];
+                do {
+                    const char *name(tag.Name());
+                    [tags_ addObject:(NSString *)CFCString(name)];
+                    if (role_ == nil && strncmp(name, "role::", 6) == 0)
+                        role_ = (NSString *) CFCString(name + 6);
+                    ++tag;
+                } while (!tag.end());
+            }
         _end
 
         bool changed(false);
@@ -1909,7 +2110,7 @@ struct PackageNameOrdering :
             metadata_ = [Packages_ objectForKey:key];
 
             if (metadata_ == nil) {
-                firstSeen_ = [now_ retain];
+                firstSeen_ = now_;
 
                 metadata_ = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
                     firstSeen_, @"FirstSeen",
@@ -1952,7 +2153,7 @@ struct PackageNameOrdering :
         _end
 
         _profile(Package$initWithVersion$Section)
-            section_.set(pool, iterator_.Section());
+            section_.set(pool_, iterator_.Section());
         _end
 
         essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
@@ -2009,7 +2210,7 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) longSection {
-    return LocalizeSection(section_);
+    return LocalizeSection([self section]);
 }
 
 - (NSString *) shortSection {
@@ -2041,7 +2242,7 @@ struct PackageNameOrdering :
     return version_.end() ? 0 : version_->InstalledSize;
 }
 
-- (NSString *) description {
+- (NSString *) longDescription {
     if (file_.end())
         return nil;
     pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
@@ -2061,6 +2262,10 @@ struct PackageNameOrdering :
     return [trimmed componentsJoinedByString:@"\n"];
 }
 
+- (NSString *) shortDescription {
+    return tagline_;
+}
+
 - (unichar) index {
     _profile(Package$index)
         CFStringRef name((CFStringRef) [self name]);
@@ -2196,15 +2401,11 @@ struct PackageNameOrdering :
     return name_.empty() ? id_ : name_;
 }
 
-- (NSString *) tagline {
-    return tagline_;
-}
-
 - (UIImage *) icon {
     NSString *section = [self simpleSection];
 
     UIImage *icon(nil);
-    if (icon_ != nil)
+    if (!icon_.empty())
         if ([icon_ hasPrefix:@"file:///"])
             icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
     if (icon == nil) if (section != nil)
@@ -2242,7 +2443,7 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) support {
-    return support_ != nil ? support_ : [[self source] supportForPackage:id_];
+    return !support_.empty() ? support_ : [[self source] supportForPackage:id_];
 }
 
 - (NSArray *) files {
@@ -2380,7 +2581,7 @@ struct PackageNameOrdering :
     if (range.location != NSNotFound)
         return YES;
 
-    range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
+    range = [[self shortDescription] rangeOfString:text options:MatchCompareOptions_];
     if (range.location != NSNotFound)
         return YES;
 
@@ -2428,29 +2629,8 @@ struct PackageNameOrdering :
     return [self hasTag:@"cydia::commercial"];
 }
 
-- (uint32_t) compareByPrefix {
-    size_t offset(0);
-
-    CYString &name(name_.empty() ? id_ : name_);
-    if (name.size() <= offset)
-        return 0;
-    size_t size(name.size() - offset);
-
-    char data[4];
-    if (size >= 4)
-        memcpy(data, name.data() + offset, 4);
-    else {
-        memcpy(data, name.data() + offset, size);
-        memset(data + size, 0, 4 - size);
-    }
-
-    Stifle(data[0]);
-    Stifle(data[1]);
-    Stifle(data[2]);
-    Stifle(data[3]);
-
-    /* XXX: ntohl may be more honest */
-    return OSSwapInt32(*reinterpret_cast<uint32_t *>(data));
+- (CYString &) cyname {
+    return name_.empty() ? id_ : name_;
 }
 
 - (uint32_t) compareBySection:(NSArray *)sections {
@@ -2798,7 +2978,6 @@ static NSArray *Finishes_;
         zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
         apr_pool_create(&pool_, NULL);
 
-        sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
         packages_ = [[NSMutableArray alloc] init];
 
         int fds[2];
@@ -2871,7 +3050,10 @@ static NSArray *Finishes_;
 }
 
 - (NSArray *) sources {
-    return [sources_ allValues];
+    NSMutableArray *sources([NSMutableArray arrayWithCapacity:sources_.size()]);
+    for (SourceMap::const_iterator i(sources_.begin()); i != sources_.end(); ++i)
+        [sources addObject:i->second];
+    return sources;
 }
 
 - (NSArray *) issues {
@@ -2943,6 +3125,9 @@ static NSArray *Finishes_;
         ++era_;
     }
 
+    [packages_ removeAllObjects];
+    sources_.clear();
+
     _error->Discard();
 
     delete list_;
@@ -2959,6 +3144,11 @@ static NSArray *Finishes_;
     delete policy_;
     policy_ = NULL;
 
+    if (now_ != nil) {
+        [now_ release];
+        now_ = nil;
+    }
+
     cache_.Close();
 
     apr_pool_clear(pool_);
@@ -3012,23 +3202,18 @@ static NSArray *Finishes_;
     }
 
     _trace();
-    {
-        std::string lists(_config->FindDir("Dir::State::lists"));
-
-        [sources_ removeAllObjects];
-        for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
-            std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
-            for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
-                if (debPackagesIndex *packages = dynamic_cast<debPackagesIndex *>(*index))
-                    [sources_
-                        setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
-                        forKey:[NSString stringWithFormat:@"%s%s",
-                            lists.c_str(),
-                            URItoFileName(packages->IndexURI("Packages")).c_str()
-                        ]
-                    ];
-        }
+
+    for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
+        std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
+        for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
+            // XXX: this could be more intelligent
+            if (dynamic_cast<debPackagesIndex *>(*index) != NULL) {
+                pkgCache::PkgFileIterator cached((*index)->FindInCache(cache_));
+                if (!cached.end())
+                    sources_[cached->ID] = [[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease];
+            }
     }
+
     _trace();
 
     {
@@ -3037,8 +3222,6 @@ static NSArray *Finishes_;
         [packages_ release];
         packages_ = nil;*/
 
-        [packages_ removeAllObjects];
-
         _trace();
 
         for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
@@ -3054,7 +3237,9 @@ static NSArray *Finishes_;
             packages_ = [[NSArray alloc] initWithObjects:&packages.front() count:packages.size()];
         _trace();*/
 
-        [packages_ radixSortUsingSelector:@selector(compareByPrefix) withObject:NULL];
+        [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(16)];
+        [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(4)];
+        [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(0)];
 
         /*_trace();
         PrintTimes();
@@ -3066,7 +3251,9 @@ static NSArray *Finishes_;
             CFQSortArray(&packages.front(), packages.size(), sizeof(packages.front()), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare_), NULL);*/
         //std::sort(packages.begin(), packages.end(), PackageNameOrdering());
 
-        CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+        //CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+
+        CFArrayInsertionSortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
 
         //[packages_ sortUsingFunction:reinterpret_cast<NSComparisonResult (*)(id, id, void *)>(&PackageNameCompare) context:NULL];
 
@@ -3196,13 +3383,20 @@ static NSArray *Finishes_;
     [self updateWithStatus:status_];
 }
 
-- (void) updateWithStatus:(Status &)status {
+- (NSString *) updateWithStatus:(Status &)status {
     pkgSourceList list;
     _assert(list.ReadMainList());
 
     FileFd lock;
     lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
-    _assert(!_error->PendingError());
+
+    if (_error->PendingError()) {
+        std::string error;
+        if (!_error->PopMessage(error))
+            _assert(false);
+        _error->Discard();
+        return [NSString stringWithUTF8String:error.c_str()];
+    }
 
     pkgAcquire fetcher(&status);
     _assert(list.GetIndexes(&fetcher));
@@ -3223,6 +3417,8 @@ static NSArray *Finishes_;
         [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
         Changed_ = true;
     }
+
+    return nil;
 }
 
 - (void) setDelegate:(id)delegate {
@@ -3232,10 +3428,7 @@ static NSArray *Finishes_;
 }
 
 - (Source *) getSource:(pkgCache::PkgFileIterator)file {
-    if (const char *name = file.FileName())
-        if (Source *source = [sources_ objectForKey:[NSString stringWithUTF8String:name]])
-            return source;
-    return nil;
+    return sources_[file->ID];
 }
 
 @end
@@ -4187,12 +4380,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) setPackage:(Package *)package {
     [self clearPackage];
+    [package parse];
 
     Source *source = [package source];
 
     icon_ = [[package icon] retain];
     name_ = [[package name] retain];
-    description_ = [[package tagline] retain];
+    description_ = [[package shortDescription] retain];
     commercial_ = [package isCommercial];
 
     package_ = [package retain];
@@ -4317,14 +4511,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 + (int) heightForPackage:(Package *)package {
-    NSString *tagline([package tagline]);
-    int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
-#ifdef USE_BADGES
-    if ([package hasMode] || [package half])
-        return height + 96;
-    else
-#endif
-        return height + 73;
+    return 73;
 }
 
 @end
@@ -4709,6 +4896,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [buttons_ removeAllObjects];
 
     if (package != nil) {
+        [package parse];
+
         package_ = [package retain];
         name_ = [[package id] retain];
         commercial_ = [package isCommercial];
@@ -4984,6 +5173,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
         /* XXX: this is an unsafe optimization of doomy hell */
         Method method = class_getInstanceMethod([Package class], filter);
+        _assert(method != NULL);
         imp_ = method_getImplementation(method);
         _assert(imp_ != NULL);
 
@@ -5764,7 +5954,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     ];
 }
 
-- (void) _update_ {
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"refresh"])
+        [sheet dismiss];
+}
+
+- (void) _update_:(NSString *)error {
     updating_ = false;
 
     [indicator_ stopAnimation];
@@ -5786,7 +5983,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     [UIView commitAnimations];
 
-    [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+    if (error == nil)
+        [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+    else {
+        UIActionSheet *sheet = [[[UIActionSheet alloc]
+            initWithTitle:[NSString stringWithFormat:CYLocalize("COLON_DELIMITED"), CYLocalize("ERROR"), CYLocalize("REFRESH")]
+            buttons:[NSArray arrayWithObjects:
+                CYLocalize("OK"),
+            nil]
+            defaultButtonIndex:0
+            delegate:self
+            context:@"refresh"
+        ] autorelease];
+
+        [sheet setBodyText:error];
+        [sheet popupAlertAnimated:YES];
+
+        [self reloadButtons];
+    }
 }
 
 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
@@ -5875,11 +6089,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     Status status;
     status.setDelegate(self);
 
-    [database_ updateWithStatus:status];
+    NSString *error([database_ updateWithStatus:status]);
 
     [self
-        performSelectorOnMainThread:@selector(_update_)
-        withObject:nil
+        performSelectorOnMainThread:@selector(_update_:)
+        withObject:error
         waitUntilDone:NO
     ];
 }
@@ -5919,10 +6133,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return !updating_;
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
-}
-
 - (void) _setProgressTitle:(NSString *)title {
     [prompt_ setText:title];
 }
@@ -6416,7 +6626,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             [packages_ addObject:package];
 
     _trace();
-    [packages_ radixSortUsingFunction:reinterpret_cast<uint32_t (*)(id, void *)>(&PackageChangesRadix) withArgument:NULL];
+    [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackageChangesRadix) withContext:NULL];
     _trace();
 
     Section *upgradable = [[[Section alloc] initWithName:CYLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
@@ -7029,7 +7239,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     ManageView *manage_;
     SearchView *search_;
 
-    PackageView *package_;
+    NSMutableArray *details_;
 }
 
 @end
@@ -7409,19 +7619,30 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) setPackageView:(PackageView *)view {
-    if (package_ == nil)
-        package_ = [view retain];
+    WebThreadLock();
+    [view setPackage:nil];
+    if ([details_ count] < 3)
+        [details_ addObject:view];
+    WebThreadUnlock();
+}
+
+- (PackageView *) _packageView {
+    return [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
 }
 
 - (PackageView *) packageView {
     PackageView *view;
+    size_t count([details_ count]);
 
-    if (package_ == nil)
-        view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
-    else {
-        return package_;
-        view = [package_ autorelease];
-        package_ = nil;
+    if (count == 0) {
+        view = [self _packageView];
+      renew:
+        [details_ addObject:[self _packageView]];
+    } else {
+        view = [[[details_ lastObject] retain] autorelease];
+        [details_ removeLastObject];
+        if (count == 1)
+            goto renew;
     }
 
     return view;
@@ -7559,7 +7780,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         withClass:[ManageView class]
     ] retain];
 
-    [self setPackageView:[self packageView]];
+    details_ = [[NSMutableArray alloc] initWithCapacity:4];
+    [details_ addObject:[self _packageView]];
+    [details_ addObject:[self _packageView]];
 
     PrintTimes();
 
@@ -7969,7 +8192,7 @@ void $UIWebDocumentView$_setUIKitDelegate$(UIWebDocumentView *self, SEL sel, id
 int main(int argc, char *argv[]) { _pooled
     _trace();
 
-    PackageName = reinterpret_cast<CFStringRef (*)(Package *, SEL)>(method_getImplementation(class_getInstanceMethod([Package class], @selector(name))));
+    PackageName = reinterpret_cast<CYString &(*)(Package *, SEL)>(method_getImplementation(class_getInstanceMethod([Package class], @selector(cyname))));
 
     /* Library Hacks {{{ */
     class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");