]> git.saurik.com Git - cydia.git/blobdiff - MobileCydia.mm
Clean up all of the old _profile/PrintTimes logic.
[cydia.git] / MobileCydia.mm
index e207fa33fe2bca1861382719f9aa7f97505accc7..50fcb6fec2443e072d24e93c4582464b062e8fd3 100644 (file)
@@ -159,7 +159,7 @@ class ProfileTime {
 
     void Print() {
         if (total_ != 0)
-            std::cerr << std::setw(5) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl;
+            std::cerr << std::setw(7) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl;
         total_ = 0;
         count_ = 0;
     }
@@ -205,7 +205,7 @@ extern NSString *Cydia_;
 
 #define ForRelease 1
 #define TraceLogging (1 && !ForRelease)
-#define HistogramInsertionSort (!ForRelease ? 0 : 0)
+#define HistogramInsertionSort (0 && !ForRelease)
 #define ProfileTimes (0 && !ForRelease)
 #define ForSaurik (0 && !ForRelease)
 #define LogBrowser (0 && !ForRelease)
@@ -1503,7 +1503,10 @@ static void PackageImport(const void *key, const void *value, void *context) {
         base_.set(pool, file);
 
         pkgAcquire acquire;
+        _profile(Source$setMetaIndex$GetIndexes)
         dindex->GetIndexes(&acquire, true);
+        _end
+        _profile(Source$setMetaIndex$DescURI)
         for (pkgAcquire::ItemIterator item(acquire.ItemsBegin()); item != acquire.ItemsEnd(); item++) {
             std::string file((*item)->DescURI());
             files_.insert(file);
@@ -1514,6 +1517,7 @@ static void PackageImport(const void *key, const void *value, void *context) {
             files_.insert(file + ".gz");
             files_.insert(file + "Index");
         }
+        _end
 
         FileFd fd;
         if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly))
@@ -1568,7 +1572,9 @@ static void PackageImport(const void *key, const void *value, void *context) {
         database_ = database;
         index_ = index;
 
+        _profile(Source$initWithMetaIndex$setMetaIndex)
         [self setMetaIndex:index inPool:pool];
+        _end
     } return self;
 }
 
@@ -1949,7 +1955,7 @@ struct ParsedPackage {
 
 @interface Package : NSObject {
     uint32_t era_ : 25;
-    uint32_t role_ : 3;
+    @public uint32_t role_ : 3;
     uint32_t essential_ : 1;
     uint32_t obsolete_ : 1;
     uint32_t ignored_ : 1;
@@ -1970,6 +1976,7 @@ struct ParsedPackage {
 
     CYString latest_;
     CYString installed_;
+    time_t updated_;
 
     const char *section_;
     _transient NSString *section$_;
@@ -2059,12 +2066,6 @@ struct ParsedPackage {
 - (void) install;
 - (void) remove;
 
-- (bool) isUnfilteredAndSearchedForBy:(NSArray *)query;
-- (bool) isUnfilteredAndSelectedForBy:(NSString *)search;
-- (bool) isInstalledAndUnfiltered:(NSNumber *)number;
-- (bool) isVisibleInSection:(NSString *)section source:(Source *)source;
-- (bool) isVisibleInSource:(Source *)source;
-
 @end
 
 uint32_t PackageChangesRadix(Package *self, void *) {
@@ -2458,13 +2459,21 @@ struct PackageNameOrdering :
         _profile(Package$initWithVersion$Metadata)
             const char *mixed(iterator.Name());
             size_t size(strlen(mixed));
-            char lower[size + 1];
+            static const size_t prefix(sizeof("/var/lib/dpkg/info/") - 1);
+            char lower[prefix + size + 5 + 1];
 
             for (size_t i(0); i != size; ++i)
-                lower[i] = mixed[i] | 0x20;
-            lower[size] = '\0';
+                lower[prefix + i] = mixed[i] | 0x20;
+
+            if (!installed_.empty()) {
+                memcpy(lower, "/var/lib/dpkg/info/", prefix);
+                memcpy(lower + prefix + size, ".list", 6);
+                struct stat info;
+                if (stat(lower, &info) != -1)
+                    updated_ = info.st_birthtime;
+            }
 
-            PackageValue *metadata(PackageFind(lower, size));
+            PackageValue *metadata(PackageFind(lower + prefix, size));
             metadata_ = metadata;
 
             id_.set(NULL, metadata->name_, size);
@@ -3010,6 +3019,10 @@ struct PackageNameOrdering :
     return source_ == (Source *) [NSNull null] ? nil : source_;
 }
 
+- (uint32_t) updated {
+    return std::numeric_limits<uint32_t>::max() - updated_;
+}
+
 - (uint32_t) rank {
     return rank_;
 }
@@ -3140,62 +3153,6 @@ struct PackageNameOrdering :
     cache->MarkDelete(iterator_, true);
 } }
 
-- (bool) isUnfilteredAndSearchedForBy:(NSArray *)query {
-    _profile(Package$isUnfilteredAndSearchedForBy)
-        bool value(true);
-
-        _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
-            value &= [self unfiltered];
-        _end
-
-        _profile(Package$isUnfilteredAndSearchedForBy$Match)
-            value &= [self matches:query];
-        _end
-
-        return value;
-    _end
-}
-
-- (bool) isUnfilteredAndSelectedForBy:(NSString *)search {
-    if ([search length] == 0)
-        return false;
-
-    _profile(Package$isUnfilteredAndSelectedForBy)
-        bool value(true);
-
-        _profile(Package$isUnfilteredAndSelectedForBy$Unfiltered)
-            value &= [self unfiltered];
-        _end
-
-        _profile(Package$isUnfilteredAndSelectedForBy$Match)
-            value &= [[self name] compare:search options:MatchCompareOptions_ range:NSMakeRange(0, [search length])] == NSOrderedSame;
-        _end
-
-        return value;
-    _end
-}
-
-- (bool) isInstalledAndUnfiltered:(NSNumber *)number {
-    return ![self uninstalled] && role_ <= ([number boolValue] ? 1 : 3);
-}
-
-- (bool) isVisibleInSection:(NSString *)name source:(Source *)source {
-    NSString *section([self section]);
-
-    return (
-        name == nil ||
-        section == nil && [name length] == 0 ||
-        [name isEqualToString:section]
-    ) && (
-        source == nil ||
-        [self source] == source
-    ) && [self visible];
-}
-
-- (bool) isVisibleInSource:(Source *)source {
-    return [self source] == source && [self visible];
-}
-
 @end
 /* }}} */
 /* Section Class {{{ */
@@ -3638,18 +3595,26 @@ class CydiaLogCleaner :
     NSString *title(UCLocalize("DATABASE"));
 
     list_ = new pkgSourceList();
+    _profile(reloadDataWithInvocation$ReadMainList)
     if ([self popErrorWithTitle:title forOperation:list_->ReadMainList()])
         return;
+    _end
 
+    _profile(reloadDataWithInvocation$Source$initWithMetaIndex)
     for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
         Source *object([[[Source alloc] initWithMetaIndex:*source forDatabase:self inPool:pool_] autorelease]);
         [sourceList_ addObject:object];
     }
+    _end
 
     _trace();
     OpProgress progress;
+    bool opened;
   open:
-    if (!cache_.Open(progress, true)) {
+    _profile(reloadDataWithInvocation$pkgCacheFile)
+        opened = cache_.Open(progress, true);
+    _end
+    if (!opened) {
         // XXX: what if there are errors, but Open() == true? this should be merged with popError:
         while (!_error->empty()) {
             std::string error;
@@ -3696,20 +3661,26 @@ class CydiaLogCleaner :
         return;
     }
 
+    _profile(reloadDataWithInvocation$pkgApplyStatus)
     if ([self popErrorWithTitle:title forOperation:pkgApplyStatus(cache_)])
         return;
+    _end
 
     if (cache_->BrokenCount() != 0) {
+        _profile(pkgApplyStatus$pkgFixBroken)
         if ([self popErrorWithTitle:title forOperation:pkgFixBroken(cache_)])
             return;
+        _end
 
         if (cache_->BrokenCount() != 0) {
             [delegate_ addProgressEventOnMainThread:[CydiaProgressEvent eventWithMessage:UCLocalize("STILL_BROKEN_EX") ofType:kCydiaProgressEventTypeError] forTask:title];
             return;
         }
 
+        _profile(pkgApplyStatus$pkgMinimizeUpgrade)
         if ([self popErrorWithTitle:title forOperation:pkgMinimizeUpgrade(cache_)])
             return;
+        _end
     }
 
     for (Source *object in (id) sourceList_) {
@@ -3729,14 +3700,13 @@ class CydiaLogCleaner :
         packages.reserve(std::max(10000U, [packages_ count] + 1000));
         packages_ = nil;*/
 
-        _trace();
-
+        _profile(reloadDataWithInvocation$packageWithIterator)
         for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
             if (Package *package = [Package packageWithIterator:iterator withZone:zone_ inPool:pool_ database:self])
                 //packages.push_back(package);
                 CFArrayAppendValue(packages_, CFRetain(package));
+        _end
 
-        _trace();
 
         /*if (packages.empty())
             packages_ = [[NSArray alloc] init];
@@ -3744,35 +3714,43 @@ class CydiaLogCleaner :
             packages_ = [[NSArray alloc] initWithObjects:&packages.front() count:packages.size()];
         _trace();*/
 
-        [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(16)];
+        _profile(reloadDataWithInvocation$radix$8)
+        [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(8)];
+        _end
+
+        _profile(reloadDataWithInvocation$radix$4)
         [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(4)];
-        [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(0)];
+        _end
 
-        /*_trace();
-        PrintTimes();
-        _trace();*/
+        _profile(reloadDataWithInvocation$radix$0)
+        [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(0)];
+        _end
 
-        _trace();
+        _profile(reloadDataWithInvocation$insertion)
+        CFArrayInsertionSortValues(packages_, CFRangeMake(0, CFArrayGetCount(packages_)), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+        _end
 
-        /*if (!packages.empty())
-            CFQSortArray(&packages.front(), packages.size(), sizeof(packages.front()), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare_), NULL);*/
-        //std::sort(packages.begin(), packages.end(), PackageNameOrdering());
+        /*_profile(reloadDataWithInvocation$CFQSortArray)
+        CFQSortArray(&packages.front(), packages.size(), sizeof(packages.front()), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare_), NULL);
+        _end*/
 
-        //CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+        /*_profile(reloadDataWithInvocation$stdsort)
+        std::sort(packages.begin(), packages.end(), PackageNameOrdering());
+        _end*/
 
-        CFArrayInsertionSortValues(packages_, CFRangeMake(0, CFArrayGetCount(packages_)), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+        /*_profile(reloadDataWithInvocation$CFArraySortValues)
+        CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+        _end*/
 
-        //[packages_ sortUsingFunction:reinterpret_cast<NSComparisonResult (*)(id, id, void *)>(&PackageNameCompare) context:NULL];
+        /*_profile(reloadDataWithInvocation$sortUsingFunction)
+        [packages_ sortUsingFunction:reinterpret_cast<NSComparisonResult (*)(id, id, void *)>(&PackageNameCompare) context:NULL];
+        _end*/
 
-        _trace();
 
         size_t count(CFArrayGetCount(packages_));
         MetaFile_->active_ = count;
-
         for (size_t index(0); index != count; ++index)
             [(Package *) CFArrayGetValueAtIndex(packages_, index) setIndex:index];
-
-        _trace();
     }
 } }
 
@@ -6230,10 +6208,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _transient Database *database_;
     unsigned era_;
     _H<NSArray> packages_;
-    _H<NSMutableArray> sections_;
+    _H<NSArray> sections_;
     _H<UITableView, 2> list_;
     _H<NSMutableArray> index_;
-    _H<NSMutableDictionary> indices_;
     _H<NSString> title_;
     unsigned reloading_;
 }
@@ -6243,6 +6220,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) resetCursor;
 - (void) clearData;
 
+- (NSArray *) sectionsForPackages:(NSMutableArray *)packages;
+
 @end
 
 @implementation PackageListController
@@ -6457,7 +6436,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     packages_ = nil;
     sections_ = nil;
     index_ = nil;
-    indices_ = nil;
 
     [super releaseSubviews];
 }
@@ -6488,7 +6466,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         return;
     }
 
-    NSArray *packages;
+    NSMutableArray *packages;
 
   reload:
     if ([self shouldYield]) {
@@ -6518,9 +6496,21 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     reloading_ = 0;
 
     packages_ = packages;
+    sections_ = [self sectionsForPackages:packages];
 
-    indices_ = [NSMutableDictionary dictionaryWithCapacity:32];
-    sections_ = [NSMutableArray arrayWithCapacity:16];
+    [self updateHeight];
+
+    _profile(PackageTable$reloadData$List)
+        [(UITableView *) list_ setDataSource:self];
+        [list_ reloadData];
+    _end
+}
+
+    PrintTimes();
+}
+
+- (NSArray *) sectionsForPackages:(NSMutableArray *)packages {
+    NSMutableArray *sections([NSMutableArray arrayWithCapacity:16]);
 
     Section *section = nil;
 
@@ -6533,12 +6523,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         int secidx = -1;
 
         _profile(PackageTable$reloadData$Section)
-            for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
+            for (size_t offset(0), end([packages count]); offset != end; ++offset) {
                 Package *package;
                 int index;
 
                 _profile(PackageTable$reloadData$Section$Package)
-                    package = [packages_ objectAtIndex:offset];
+                    package = [packages objectAtIndex:offset];
                     index = [collation sectionForObject:package collationStringSelector:@selector(name)];
                 _end
 
@@ -6550,7 +6540,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                     _end
 
                     _profile(PackageTable$reloadData$Section$Add)
-                        [sections_ addObject:section];
+                        [sections addObject:section];
                     _end
                 }
 
@@ -6565,16 +6555,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         bool sectioned([self showsSections]);
         if (!sectioned) {
             section = [[[Section alloc] initWithName:nil localize:false] autorelease];
-            [sections_ addObject:section];
+            [sections addObject:section];
         }
 
         _profile(PackageTable$reloadData$Section)
-            for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
+            for (size_t offset(0), end([packages count]); offset != end; ++offset) {
                 Package *package;
                 unichar index;
 
                 _profile(PackageTable$reloadData$Section$Package)
-                    package = [packages_ objectAtIndex:offset];
+                    package = [packages objectAtIndex:offset];
                     index = [package index];
                 _end
 
@@ -6584,10 +6574,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                     _end
 
                     [index_ addObject:[section name]];
-                    //[indices_ setObject:[NSNumber numberForInt:[sections_ count]] forKey:index];
 
                     _profile(PackageTable$reloadData$Section$Add)
-                        [sections_ addObject:section];
+                        [sections addObject:section];
                     _end
                 }
 
@@ -6596,13 +6585,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         _end
     }
 
-    [self updateHeight];
-
-    _profile(PackageTable$reloadData$List)
-        [(UITableView *) list_ setDataSource:self];
-        [list_ reloadData];
-    _end
-} }
+    return sections;
+}
 
 - (void) reloadData {
     [super reloadData];
@@ -6629,113 +6613,61 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Filtered Package List Controller {{{ */
+typedef Function<bool, Package *> PackageFilter;
+typedef Function<void, NSMutableArray *> PackageSorter;
 @interface FilteredPackageListController : PackageListController {
-    SEL filter_;
-    IMP imp_;
-    _H<NSObject> object_;
-    _H<NSObject> stuff_;
+    PackageFilter filter_;
+    PackageSorter sorter_;
 }
 
-- (void) setObject:(id)object;
-- (void) setStuff:(id)object;
-- (void) setObject:(id)object andStuff:(id)stuff;
-
-- (void) setObject:(id)object forFilter:(SEL)filter;
-- (void) setObject:(id)object andStuff:(id)stuff forFilter:(SEL)filter;
-
-- (SEL) filter;
-- (void) setFilter:(SEL)filter;
+- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(PackageFilter)filter;
 
-- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
+- (void) setFilter:(PackageFilter)filter;
+- (void) setSorter:(PackageSorter)sorter;
 
 @end
 
 @implementation FilteredPackageListController
 
-- (SEL) filter {
-    return filter_;
-}
-
-- (void) setFilter:(SEL)filter {
+- (void) setFilter:(PackageFilter)filter {
 @synchronized (self) {
     filter_ = filter;
-
-    /* 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);
-} }
-
-- (void) setObject:(id)object {
-@synchronized (self) {
-    object_ = object;
-} }
-
-- (void) setStuff:(id)stuff {
-@synchronized (self) {
-    stuff_ = stuff;
 } }
 
-- (void) setObject:(id)object andStuff:(id)stuff {
+- (void) setSorter:(PackageSorter)sorter {
 @synchronized (self) {
-    object_ = object;
-    stuff_ = stuff;
-} }
-
-- (void) setObject:(id)object forFilter:(SEL)filter {
-@synchronized (self) {
-    [self setFilter:filter];
-    object_ = object;
-} }
-
-- (void) setObject:(id)object andStuff:(id)stuff forFilter:(SEL)filter {
-@synchronized (self) {
-    [self setFilter:filter];
-    object_ = object;
-    stuff_ = stuff;
+    sorter_ = sorter;
 } }
 
 - (NSMutableArray *) _reloadPackages {
 @synchronized (database_) {
     era_ = [database_ era];
-    NSArray *packages([database_ packages]);
 
+    NSArray *packages([database_ packages]);
     NSMutableArray *filtered([NSMutableArray arrayWithCapacity:[packages count]]);
 
-    IMP imp;
-    SEL filter;
-    _H<NSObject> object;
-    _H<NSObject> stuff;
+    PackageFilter filter;
+    PackageSorter sorter;
 
     @synchronized (self) {
-        imp = imp_;
         filter = filter_;
-        object = object_;
-        stuff = stuff_;
+        sorter = sorter_;
     }
 
     _profile(PackageTable$reloadData$Filter)
         for (Package *package in packages)
-            if ([package valid] && (*reinterpret_cast<bool (*)(id, SEL, id, id)>(imp))(package, filter, object, stuff))
+            if ([package valid] && filter(package))
                 [filtered addObject:package];
     _end
 
+    if (sorter)
+        sorter(filtered);
     return filtered;
 } }
 
-- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
-    if ((self = [super initWithDatabase:database title:title]) != nil) {
-        [self setFilter:filter];
-        object_ = object;
-    } return self;
-}
-
-- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object with:(id)stuff {
+- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(PackageFilter)filter {
     if ((self = [super initWithDatabase:database title:title]) != nil) {
         [self setFilter:filter];
-        object_ = object;
-        stuff_ = stuff;
     } return self;
 }
 
@@ -7149,14 +7081,29 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     else
         title = UCLocalize("NO_SECTION");
 
-    if ((self = [super initWithDatabase:database title:title filter:@selector(isVisibleInSection:source:) with:section with:source]) != nil) {
+    if ((self = [super initWithDatabase:database title:title]) != nil) {
         key_ = [source key];
         section_ = section;
     } return self;
 }
 
 - (void) reloadData {
-    [super setStuff:[database_ sourceWithKey:key_]];
+    Source *source([database_ sourceWithKey:key_]);
+    _H<NSString> name(section_);
+
+    [self setFilter:[=](Package *package) {
+        NSString *section([package section]);
+
+        return (
+            name == nil ||
+            section == nil && [name length] == 0 ||
+            [name isEqualToString:section]
+        ) && (
+            source == nil ||
+            [package source] == source
+        ) && [package visible];
+    }];
+
     [super reloadData];
 }
 
@@ -7378,15 +7325,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 /* }}} */
 
 /* Changes Controller {{{ */
-@interface ChangesController : CyteViewController <
-    UITableViewDataSource,
-    UITableViewDelegate
-> {
-    _transient Database *database_;
-    unsigned era_;
-    _H<NSMutableArray> packages_;
-    _H<NSMutableArray> sections_;
-    _H<UITableView, 2> list_;
+@interface ChangesController : FilteredPackageListController {
     unsigned upgrades_;
 }
 
@@ -7396,30 +7335,12 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 @implementation ChangesController
 
-- (NSURL *) navigationURL {
-    return [NSURL URLWithString:@"cydia://changes"];
-}
-
-- (void) viewDidAppear:(BOOL)animated {
-    [super viewDidAppear:animated];
-    [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
-}
-
-- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
-    NSInteger count([sections_ count]);
-    return count == 0 ? 1 : count;
-}
-
-- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
-    if ([sections_ count] == 0)
-        return nil;
-    return [[sections_ objectAtIndex:section] name];
+- (NSURL *) referrerURL {
+    return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/changes/", UI_]];
 }
 
-- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
-    if ([sections_ count] == 0)
-        return 0;
-    return [[sections_ objectAtIndex:section] count];
+- (NSURL *) navigationURL {
+    return [NSURL URLWithString:@"cydia://changes"];
 }
 
 - (Package *) packageAtIndexPath:(NSIndexPath *)path {
@@ -7435,24 +7356,6 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     return [[[packages_ objectAtIndex:([section row] + row)] retain] autorelease];
 } }
 
-- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
-    PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
-    if (cell == nil)
-        cell = [[[PackageCell alloc] init] autorelease];
-
-    Package *package([database_ packageWithName:[[self packageAtIndexPath:path] id]]);
-    [cell setPackage:package asSummary:false];
-    return cell;
-}
-
-- (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path {
-    Package *package([self packageAtIndexPath:path]);
-    CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id] withReferrer:[NSString stringWithFormat:@"%@/#!/changes/", UI_]] autorelease]);
-    [view setDelegate:delegate_];
-    [[self navigationController] pushViewController:view animated:YES];
-    return path;
-}
-
 - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
     NSString *context([alert context]);
 
@@ -7491,84 +7394,38 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [[self navigationItem] setRightBarButtonItem:nil animated:YES];
 }
 
-- (void) loadView {
-    UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]);
-    [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
-    [self setView:view];
-
-    list_ = [[[UITableView alloc] initWithFrame:[view bounds] style:UITableViewStylePlain] autorelease];
-    [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
-    [list_ setRowHeight:73];
-    [(UITableView *) list_ setDataSource:self];
-    [list_ setDelegate:self];
-    [view addSubview:list_];
+- (bool) shouldYield {
+    return true;
 }
 
-- (void) viewDidLoad {
-    [super viewDidLoad];
-
-    [[self navigationItem] setTitle:UCLocalize("CHANGES")];
+- (bool) shouldBlock {
+    return true;
 }
 
-- (void) releaseSubviews {
-    list_ = nil;
-
-    packages_ = nil;
-    sections_ = nil;
+- (void) useFilter {
+@synchronized (self) {
+    [self setFilter:[](Package *package) {
+        return [package upgradableAndEssential:YES] || [package visible];
+    }];
 
-    [super releaseSubviews];
-}
+    [self setSorter:[](NSMutableArray *packages) {
+        [packages radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackageChangesRadix) withContext:NULL];
+    }];
+} }
 
 - (id) initWithDatabase:(Database *)database {
-    if ((self = [super init]) != nil) {
-        database_ = database;
+    if ((self = [super initWithDatabase:database title:UCLocalize("CHANGES")]) != nil) {
+        [self useFilter];
     } return self;
 }
 
-- (NSMutableArray *) _reloadPackages {
-@synchronized (database_) {
-    era_ = [database_ era];
-    NSArray *packages([database_ packages]);
-
-    NSMutableArray *filtered([NSMutableArray arrayWithCapacity:[packages count]]);
-
-    _trace();
-    _profile(ChangesController$_reloadPackages$Filter)
-        for (Package *package in packages)
-            if ([package upgradableAndEssential:YES] || [package visible])
-                CFArrayAppendValue((CFMutableArrayRef) filtered, package);
-    _end
-    _trace();
-    _profile(ChangesController$_reloadPackages$radixSort)
-        [filtered radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackageChangesRadix) withContext:NULL];
-    _end
-    _trace();
-
-    return filtered;
-} }
-
-- (void) _reloadData {
+- (void) reloadData {
     [self setLeftBarButtonItem];
+    [super reloadData];
+}
 
-    NSMutableArray *packages;
-
-  reload:
-    if (true) {
-        UIProgressHUD *hud([delegate_ addProgressHUD]);
-        [hud setText:UCLocalize("LOADING")];
-        //NSLog(@"HUD:%@::%@", delegate_, hud);
-        packages = [self yieldToSelector:@selector(_reloadPackages)];
-        [delegate_ removeProgressHUD:hud];
-    } else {
-        packages = [self _reloadPackages];
-    }
-
-@synchronized (database_) {
-    if (era_ != [database_ era])
-        goto reload;
-
-    packages_ = packages;
-    sections_ = [NSMutableArray arrayWithCapacity:16];
+- (NSArray *) sectionsForPackages:(NSMutableArray *)packages {
+    NSMutableArray *sections([NSMutableArray arrayWithCapacity:16]);
 
     Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
     Section *ignored = nil;
@@ -7580,8 +7437,8 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
     CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle));
 
-    for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
-        Package *package = [packages_ objectAtIndex:offset];
+    for (size_t offset = 0, count = [packages count]; offset != count; ++offset) {
+        Package *package = [packages objectAtIndex:offset];
 
         BOOL uae = [package upgradableAndEssential:YES];
 
@@ -7599,7 +7456,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
                 _profile(ChangesController$reloadData$Allocate)
                     name = [NSString stringWithFormat:UCLocalize("NEW_AT"), name];
                     section = [[[Section alloc] initWithName:name row:offset localize:NO] autorelease];
-                    [sections_ addObject:section];
+                    [sections addObject:section];
                 _end
             }
 
@@ -7619,16 +7476,16 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     CFRelease(formatter);
 
     if (unseens) {
-        Section *last = [sections_ lastObject];
+        Section *last = [sections lastObject];
         size_t count = [last count];
-        [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
-        [sections_ removeLastObject];
+        [packages removeObjectsInRange:NSMakeRange([packages count] - count, count)];
+        [sections removeLastObject];
     }
 
     if ([ignored count] != 0)
-        [sections_ insertObject:ignored atIndex:0];
+        [sections insertObject:ignored atIndex:0];
     if (upgrades_ != 0)
-        [sections_ insertObject:upgradable atIndex:0];
+        [sections insertObject:upgradable atIndex:0];
 
     [list_ reloadData];
 
@@ -7639,12 +7496,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         action:@selector(upgradeButtonClicked)
     ] autorelease]) animated:YES];
 
-    PrintTimes();
-} }
-
-- (void) reloadData {
-    [super reloadData];
-    [self performSelector:@selector(_reloadData) withObject:nil afterDelay:0];
+    return sections;
 }
 
 @end
@@ -7655,6 +7507,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 > {
     _H<UISearchBar, 1> search_;
     BOOL searchloaded_;
+    bool summary_;
 }
 
 - (id) initWithDatabase:(Database *)database query:(NSString *)query;
@@ -7685,15 +7538,51 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) useSearch {
-    [self setObject:[self termsForQuery:[search_ text]] forFilter:@selector(isUnfilteredAndSearchedForBy:)];
+    _H<NSArray> query([self termsForQuery:[search_ text]]);
+    summary_ = false;
+
+@synchronized (self) {
+    [self setFilter:[=](Package *package) {
+        if (![package unfiltered])
+            return false;
+        if (![package matches:query])
+            return false;
+        return true;
+    }];
+
+    [self setSorter:[](NSMutableArray *packages) {
+        [packages radixSortUsingSelector:@selector(rank)];
+    }];
+}
+
     [self clearData];
     [self reloadData];
 }
 
+- (void) usePrefix:(NSString *)prefix {
+    _H<NSString> query(prefix);
+    summary_ = true;
+
+@synchronized (self) {
+    [self setFilter:[=](Package *package) {
+        if ([query length] == 0)
+            return false;
+        if (![package unfiltered])
+            return false;
+        if ([[package name] compare:query options:MatchCompareOptions_ range:NSMakeRange(0, [query length])] != NSOrderedSame)
+            return false;
+        return true;
+    }];
+
+    [self setSorter:nullptr];
+}
+
+    [self reloadData];
+}
+
 - (void) searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
-    [self setObject:[search_ text] forFilter:@selector(isUnfilteredAndSelectedForBy:)];
     [self clearData];
-    [self reloadData];
+    [self usePrefix:[search_ text]];
 }
 
 - (void) searchBarButtonClicked:(UISearchBar *)searchBar {
@@ -7711,8 +7600,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)text {
-    [self setObject:text forFilter:@selector(isUnfilteredAndSelectedForBy:)];
-    [self reloadData];
+    [self usePrefix:text];
 }
 
 - (bool) shouldYield {
@@ -7720,31 +7608,36 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (bool) shouldBlock {
-    return [self filter] == @selector(isUnfilteredAndSearchedForBy:);
+    return !summary_;
 }
 
 - (bool) isSummarized {
-    return [self filter] == @selector(isUnfilteredAndSelectedForBy:);
+    return summary_;
 }
 
 - (bool) showsSections {
     return false;
 }
 
-- (NSMutableArray *) _reloadPackages {
-    NSMutableArray *packages([super _reloadPackages]);
-    if ([self filter] == @selector(isUnfilteredAndSearchedForBy:))
-        [packages radixSortUsingSelector:@selector(rank)];
-    return packages;
-}
-
 - (id) initWithDatabase:(Database *)database query:(NSString *)query {
-    if ((self = [super initWithDatabase:database title:UCLocalize("SEARCH") filter:@selector(isUnfilteredAndSearchedForBy:) with:[self termsForQuery:query]])) {
+    if ((self = [super initWithDatabase:database title:UCLocalize("SEARCH")])) {
         search_ = [[[UISearchBar alloc] init] autorelease];
+        [search_ setPlaceholder:UCLocalize("SEARCH_EX")];
         [search_ setDelegate:self];
 
+        UITextField *textField;
+        if ([search_ respondsToSelector:@selector(searchField)])
+            textField = [search_ searchField];
+        else
+            textField = MSHookIvar<UITextField *>(search_, "_searchField");
+
+        [textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
+        [textField setEnablesReturnKeyAutomatically:NO];
+        [[self navigationItem] setTitleView:textField];
+
         if (query != nil)
             [search_ setText:query];
+        [self useSearch];
     } return self;
 }
 
@@ -7755,17 +7648,6 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
         searchloaded_ = YES;
         [search_ setFrame:CGRectMake(0, 0, [[self view] bounds].size.width, 44.0f)];
         [search_ layoutSubviews];
-        [search_ setPlaceholder:UCLocalize("SEARCH_EX")];
-
-        UITextField *textField;
-        if ([search_ respondsToSelector:@selector(searchField)])
-            textField = [search_ searchField];
-        else
-            textField = MSHookIvar<UITextField *>(search_, "_searchField");
-
-        [textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
-        [textField setEnablesReturnKeyAutomatically:NO];
-        [[self navigationItem] setTitleView:textField];
     }
 
     if ([self isSummarized])
@@ -7773,13 +7655,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) reloadData {
-    id object([search_ text]);
-    if ([self filter] == @selector(isUnfilteredAndSearchedForBy:))
-        object = [self termsForQuery:object];
-
-    [self setObject:object];
     [self resetCursor];
-
     [super reloadData];
 }
 
@@ -7965,6 +7841,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
 /* Installed Controller {{{ */
 @interface InstalledController : FilteredPackageListController {
+    bool sectioned_;
 }
 
 - (id) initWithDatabase:(Database *)database;
@@ -7982,14 +7859,47 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     return [NSURL URLWithString:@"cydia://installed"];
 }
 
+- (bool) showsSections {
+    return sectioned_;
+}
+
+- (void) useUpdated {
+    sectioned_ = false;
+
+@synchronized (self) {
+    [self setFilter:[](Package *package) {
+        return ![package uninstalled] && package->role_ < 7;
+    }];
+
+    [self setSorter:[](NSMutableArray *packages) {
+        [packages radixSortUsingSelector:@selector(updated)];
+    }];
+} }
+
+- (void) useFilter:(UISegmentedControl *)segmented {
+    NSInteger selected([segmented selectedSegmentIndex]);
+    if (selected == 2)
+        return [self useUpdated];
+    bool simple(selected == 0);
+    sectioned_ = true;
+
+@synchronized (self) {
+    [self setFilter:[=](Package *package) {
+        return ![package uninstalled] && package->role_ <= (simple ? 1 : 3);
+    }];
+
+    [self setSorter:nullptr];
+} }
+
 - (id) initWithDatabase:(Database *)database {
-    if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED") filter:@selector(isInstalledAndUnfiltered:) with:[NSNumber numberWithBool:YES]]) != nil) {
-        UISegmentedControl *segmented([[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:UCLocalize("SIMPLE"), UCLocalize("EXPERT"), nil]] autorelease]);
+    if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED")]) != nil) {
+        UISegmentedControl *segmented([[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:UCLocalize("USER"), UCLocalize("EXPERT"), UCLocalize("RECENT"), nil]] autorelease]);
         [segmented setSelectedSegmentIndex:0];
         [segmented setSegmentedControlStyle:UISegmentedControlStyleBar];
         [[self navigationItem] setTitleView:segmented];
 
         [segmented addTarget:self action:@selector(modeChanged:) forEvents:UIControlEventValueChanged];
+        [self useFilter:segmented];
 
         [self queueStatusDidChange];
     } return self;
@@ -8017,8 +7927,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) modeChanged:(UISegmentedControl *)segmented {
-    bool simple([segmented selectedSegmentIndex] == 0);
-    [self setObject:[NSNumber numberWithBool:simple]];
+    [self useFilter:segmented];
     [self reloadData];
 }
 
@@ -8987,6 +8896,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 }
 
 - (void) reloadDataWithInvocation:(NSInvocation *)invocation {
+_profile(reloadDataWithInvocation)
 @synchronized (self) {
     UIProgressHUD *hud(loaded_ ? [self addProgressHUD] : nil);
     if (hud != nil)
@@ -8999,6 +8909,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     [essential_ removeAllObjects];
     [broken_ removeAllObjects];
 
+    _profile(reloadDataWithInvocation$Essential)
     NSArray *packages([database_ packages]);
     for (Package *package in packages) {
         if ([package half])
@@ -9009,6 +8920,7 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
             ++changes;
         }
     }
+    _end
 
     UITabBarItem *changesItem = [[[tabbar_ viewControllers] objectAtIndex:2] tabBarItem];
     if (changes != 0) {
@@ -9028,7 +8940,11 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
 
     if (hud != nil)
         [self removeProgressHUD:hud];
-} }
+}
+_end
+
+    PrintTimes();
+}
 
 - (void) updateData {
     [self _updateData];
@@ -9049,10 +8965,13 @@ static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachabi
     if (emulated_ == nil)
         return;
 
-    [window_ addSubview:[tabbar_ view]];
     if ([window_ respondsToSelector:@selector(setRootViewController:)])
         [window_ setRootViewController:tabbar_];
-    [[emulated_ view] removeFromSuperview];
+    else {
+        [window_ addSubview:[tabbar_ view]];
+        [[emulated_ view] removeFromSuperview];
+    }
+
     emulated_ = nil;
     [window_ setUserInteractionEnabled:YES];
 }
@@ -9742,9 +9661,10 @@ _trace();
     [self setupViewControllers];
 
     emulated_ = [[[CydiaLoadingViewController alloc] init] autorelease];
-    [window_ addSubview:[emulated_ view]];
     if ([window_ respondsToSelector:@selector(setRootViewController:)])
         [window_ setRootViewController:emulated_];
+    else
+        [window_ addSubview:[emulated_ view]];
 
     [self performSelector:@selector(loadData) withObject:nil afterDelay:0];
 _trace();
@@ -9768,8 +9688,6 @@ _trace();
 
     [self reloadDataWithInvocation:nil];
     [self refreshIfPossible];
-    PrintTimes();
-
     [self disemulate];
 
     int savedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue];