git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

HexFiend/HFController.m

      1 //
      2 //  HFController.m
      3 //  HexFiend_2
      4 //
      5 //  Copyright 2007 ridiculous_fish. All rights reserved.
      6 //
      7 
      8 #import <HexFiend/HFController.h>
      9 #import <HexFiend/HFRepresenter_Internal.h>
     10 #import <HexFiend/HFByteArray_Internal.h>
     11 #import <HexFiend/HFFullMemoryByteArray.h>
     12 #import <HexFiend/HFBTreeByteArray.h>
     13 #import <HexFiend/HFFullMemoryByteSlice.h>
     14 #import <HexFiend/HFSharedMemoryByteSlice.h>
     15 #import <objc/runtime.h>
     16 #import <objc/message.h>
     17 #import <objc/objc-auto.h>
     18 
     19 /* Used for the anchor range and location */
     20 #define NO_SELECTION ULLONG_MAX
     21 
     22 #define VALIDATE_SELECTION() [self _ensureSelectionIsValid]
     23 
     24 #define BENCHMARK_BYTEARRAYS 0
     25 
     26 #define BEGIN_TRANSACTION() NSUInteger token = [self beginPropertyChangeTransaction]
     27 #define END_TRANSACTION() [self endPropertyChangeTransaction:token]
     28 
     29 static const CGFloat kScrollMultiplier = (CGFloat)1.5;
     30 
     31 static const CFTimeInterval kPulseDuration = .2;
     32 
     33 static void *KVOContextChangesAreLocked = &KVOContextChangesAreLocked;
     34 
     35 NSString * const HFPrepareForChangeInFileNotification = @"HFPrepareForChangeInFileNotification";
     36 NSString * const HFChangeInFileByteArrayKey = @"HFChangeInFileByteArrayKey";
     37 NSString * const HFChangeInFileModifiedRangesKey = @"HFChangeInFileModifiedRangesKey";
     38 NSString * const HFChangeInFileShouldCancelKey = @"HFChangeInFileShouldCancelKey";
     39 NSString * const HFChangeInFileHintKey = @"HFChangeInFileHintKey";
     40 
     41 NSString * const HFControllerDidChangePropertiesNotification = @"HFControllerDidChangePropertiesNotification";
     42 NSString * const HFControllerChangedPropertiesKey = @"HFControllerChangedPropertiesKey";
     43 
     44 
     45 typedef NS_ENUM(NSInteger, HFControllerSelectAction) {
     46     eSelectResult,
     47     eSelectAfterResult,
     48     ePreserveSelection,
     49     NUM_SELECTION_ACTIONS
     50 };
     51 
     52 @interface HFController (ForwardDeclarations)
     53 - (void)_commandInsertByteArrays:(NSArray *)byteArrays inRanges:(NSArray *)ranges withSelectionAction:(HFControllerSelectAction)selectionAction;
     54 - (void)_removeUndoManagerNotifications;
     55 - (void)_removeAllUndoOperations;
     56 - (void)_registerUndoOperationForInsertingByteArrays:(NSArray *)byteArrays inRanges:(NSArray *)ranges withSelectionAction:(HFControllerSelectAction)selectionAction;
     57 
     58 - (void)_updateBytesPerLine;
     59 - (void)_updateDisplayedRange;
     60 @end
     61 
     62 @interface NSEvent (HFLionStuff)
     63 - (CGFloat)scrollingDeltaY;
     64 - (BOOL)hasPreciseScrollingDeltas;
     65 - (CGFloat)deviceDeltaY;
     66 @end
     67 
     68 static inline Class preferredByteArrayClass(void) {
     69     return [HFBTreeByteArray class];
     70 }
     71 
     72 @implementation HFController
     73 
     74 - (void)_sharedInit {
     75     selectedContentsRanges = [[NSMutableArray alloc] initWithObjects:[HFRangeWrapper withRange:HFRangeMake(0, 0)], nil];
     76     byteArray = [[preferredByteArrayClass() alloc] init];
     77     [byteArray addObserver:self forKeyPath:@"changesAreLocked" options:0 context:KVOContextChangesAreLocked];
     78     selectionAnchor = NO_SELECTION;
     79     undoOperations = [[NSMutableSet alloc] init];
     80 }
     81 
     82 - (instancetype)init {
     83     self = [super init];
     84     [self _sharedInit];
     85     bytesPerLine = 16;
     86     bytesPerColumn = 1;
     87     _hfflags.editable = YES;
     88     _hfflags.antialias = YES;
     89     _hfflags.showcallouts = YES;
     90     _hfflags.hideNullBytes = NO;
     91     _hfflags.selectable = YES;
     92     representers = [[NSMutableArray alloc] init];
     93     [self setFont:[NSFont fontWithName:HFDEFAULT_FONT size:HFDEFAULT_FONTSIZE]];
     94     return self;
     95 }
     96 
     97 - (void)dealloc {
     98     [representers makeObjectsPerformSelector:@selector(_setController:) withObject:nil];
     99     [representers release];
    100     [selectedContentsRanges release];
    101     [self _removeUndoManagerNotifications];
    102     [self _removeAllUndoOperations];
    103     [undoOperations release];
    104     [undoManager release];
    105     [undoCoalescer release];
    106     [_font release];
    107     [byteArray removeObserver:self forKeyPath:@"changesAreLocked"];
    108     [byteArray release];
    109     [cachedData release];
    110     [additionalPendingTransactions release];
    111     [super dealloc];
    112 }
    113 
    114 - (void)encodeWithCoder:(NSCoder *)coder {
    115     HFASSERT([coder allowsKeyedCoding]);
    116     [coder encodeObject:representers forKey:@"HFRepresenters"];
    117     [coder encodeInt64:bytesPerLine forKey:@"HFBytesPerLine"];
    118     [coder encodeInt64:bytesPerColumn forKey:@"HFBytesPerColumn"];
    119     [coder encodeObject:_font forKey:@"HFFont"];
    120     [coder encodeDouble:lineHeight forKey:@"HFLineHeight"];
    121     [coder encodeBool:_hfflags.antialias forKey:@"HFAntialias"];
    122     [coder encodeBool:_hfflags.colorbytes forKey:@"HFColorBytes"];
    123     [coder encodeBool:_hfflags.showcallouts forKey:@"HFShowCallouts"];
    124     [coder encodeBool:_hfflags.hideNullBytes forKey:@"HFHidesNullBytes"];
    125     [coder encodeBool:_hfflags.livereload forKey:@"HFLiveReload"];
    126     [coder encodeInt:_hfflags.editMode forKey:@"HFEditMode"];
    127     [coder encodeBool:_hfflags.editable forKey:@"HFEditable"];
    128     [coder encodeBool:_hfflags.selectable forKey:@"HFSelectable"];
    129 }
    130 
    131 - (instancetype)initWithCoder:(NSCoder *)coder {
    132     HFASSERT([coder allowsKeyedCoding]);
    133     self = [super init];
    134     [self _sharedInit];
    135     bytesPerLine = (NSUInteger)[coder decodeInt64ForKey:@"HFBytesPerLine"];
    136     bytesPerColumn = (NSUInteger)[coder decodeInt64ForKey:@"HFBytesPerColumn"];
    137     _font = [[coder decodeObjectForKey:@"HFFont"] retain];
    138     lineHeight = (CGFloat)[coder decodeDoubleForKey:@"HFLineHeight"];
    139     _hfflags.antialias = [coder decodeBoolForKey:@"HFAntialias"];
    140     _hfflags.colorbytes = [coder decodeBoolForKey:@"HFColorBytes"];
    141     _hfflags.livereload = [coder decodeBoolForKey:@"HFLiveReload"];
    142     
    143     if ([coder containsValueForKey:@"HFEditMode"])
    144         _hfflags.editMode = [coder decodeIntForKey:@"HFEditMode"];
    145     else {
    146         _hfflags.editMode = ([coder decodeBoolForKey:@"HFOverwriteMode"]
    147                              ? HFOverwriteMode : HFInsertMode);
    148     }
    149 
    150     _hfflags.editable = [coder decodeBoolForKey:@"HFEditable"];
    151     _hfflags.selectable = [coder decodeBoolForKey:@"HFSelectable"];
    152     _hfflags.hideNullBytes = [coder decodeBoolForKey:@"HFHidesNullBytes"];
    153     representers = [[coder decodeObjectForKey:@"HFRepresenters"] retain];
    154     return self;
    155 }
    156 
    157 - (NSArray *)representers {
    158     return [[representers copy] autorelease];
    159 }
    160 
    161 - (void)notifyRepresentersOfChanges:(HFControllerPropertyBits)bits {
    162     FOREACH(HFRepresenter*, rep, representers) {
    163         [rep controllerDidChange:bits];
    164     }
    165     
    166     /* Post the HFControllerDidChangePropertiesNotification */
    167     NSNumber *number = [[NSNumber alloc] initWithUnsignedInteger:bits];
    168     NSDictionary *userInfo = [[NSDictionary alloc] initWithObjects:&number forKeys:(id *)&HFControllerChangedPropertiesKey count:1];
    169     [number release];
    170     [[NSNotificationCenter defaultCenter] postNotificationName:HFControllerDidChangePropertiesNotification object:self userInfo:userInfo];
    171     [userInfo release];
    172 }
    173 
    174 - (void)_firePropertyChanges {
    175     NSMutableArray *pendingTransactions = additionalPendingTransactions;
    176     NSUInteger pendingTransactionCount = [pendingTransactions count];
    177     additionalPendingTransactions = nil;
    178     HFControllerPropertyBits propertiesToUpdate = propertiesToUpdateInCurrentTransaction;
    179     propertiesToUpdateInCurrentTransaction = 0;
    180     if (pendingTransactionCount > 0 || propertiesToUpdate != 0) {
    181         BEGIN_TRANSACTION();
    182         while (pendingTransactionCount--) {
    183             HFControllerPropertyBits propertiesInThisTransaction = [pendingTransactions[0] unsignedIntegerValue];
    184             [pendingTransactions removeObjectAtIndex:0];
    185             HFASSERT(propertiesInThisTransaction != 0);
    186             [self notifyRepresentersOfChanges:propertiesInThisTransaction];
    187         }
    188         [pendingTransactions release];
    189         if (propertiesToUpdate) {
    190             [self notifyRepresentersOfChanges:propertiesToUpdate];
    191         }
    192         END_TRANSACTION();
    193     }
    194 }
    195 
    196 /* Inserts a "fence" so that all prior property change bits will be complete before any new ones */
    197 - (void)_insertPropertyChangeFence {
    198     if (currentPropertyChangeToken == 0) {
    199         HFASSERT(additionalPendingTransactions == nil);
    200         /* There can be no prior property changes */
    201         HFASSERT(propertiesToUpdateInCurrentTransaction == 0);
    202         return;
    203     }
    204     if (propertiesToUpdateInCurrentTransaction == 0) {
    205         /* Nothing to fence */
    206         return;
    207     }
    208     if (additionalPendingTransactions == nil) additionalPendingTransactions = [[NSMutableArray alloc] init];
    209     [additionalPendingTransactions addObject:@(propertiesToUpdateInCurrentTransaction)];
    210     propertiesToUpdateInCurrentTransaction = 0;
    211 }
    212 
    213 - (void)_addPropertyChangeBits:(HFControllerPropertyBits)bits {
    214     propertiesToUpdateInCurrentTransaction |= bits;
    215     if (currentPropertyChangeToken == 0) {
    216         [self _firePropertyChanges];
    217     }
    218 }
    219 
    220 - (NSUInteger)beginPropertyChangeTransaction {
    221     HFASSERT(currentPropertyChangeToken < NSUIntegerMax);
    222     return ++currentPropertyChangeToken;
    223 }
    224 
    225 - (void)endPropertyChangeTransaction:(NSUInteger)token {
    226     if (currentPropertyChangeToken != token) {
    227         [NSException raise:NSInvalidArgumentException format:@"endPropertyChangeTransaction passed token %lu, but expected token %lu", (unsigned long)token, (unsigned long)currentPropertyChangeToken];
    228     }
    229     HFASSERT(currentPropertyChangeToken > 0);
    230     if (--currentPropertyChangeToken == 0) [self _firePropertyChanges];
    231 }
    232 
    233 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    234     if (context == KVOContextChangesAreLocked) {
    235         HFASSERT([keyPath isEqual:@"changesAreLocked"]);
    236         [self _addPropertyChangeBits:HFControllerEditable];
    237     }
    238     else {
    239         [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    240     }
    241 }
    242 
    243 - (void)addRepresenter:(HFRepresenter *)representer {
    244     REQUIRE_NOT_NULL(representer);
    245     HFASSERT([representers indexOfObjectIdenticalTo:representer] == NSNotFound);
    246     HFASSERT([representer controller] == nil);
    247     [representer _setController:self];
    248     [representers addObject:representer];
    249     [representer controllerDidChange: -1];
    250 }
    251 
    252 - (void)removeRepresenter:(HFRepresenter *)representer {
    253     REQUIRE_NOT_NULL(representer);    
    254     HFASSERT([representers indexOfObjectIdenticalTo:representer] != NSNotFound);
    255     [representers removeObjectIdenticalTo:representer];
    256     [representer _setController:nil];
    257 }
    258 
    259 - (HFRange)_maximumDisplayedRangeSet {
    260     unsigned long long contentsLength = [self contentsLength];
    261     HFRange maximumDisplayedRangeSet = HFRangeMake(0, HFRoundUpToNextMultipleSaturate(contentsLength, bytesPerLine));
    262     return maximumDisplayedRangeSet;
    263 }
    264 
    265 - (unsigned long long)totalLineCount {
    266     return HFDivideULLRoundingUp(HFRoundUpToNextMultipleSaturate([self contentsLength] - 1, bytesPerLine), bytesPerLine);
    267 }
    268 
    269 - (HFFPRange)displayedLineRange {
    270 #if ! NDEBUG
    271     HFASSERT(displayedLineRange.location >= 0);
    272     HFASSERT(displayedLineRange.length >= 0);
    273     HFASSERT(displayedLineRange.location + displayedLineRange.length <= HFULToFP([self totalLineCount]));
    274 #endif
    275     return displayedLineRange;
    276 }
    277 
    278 - (void)setDisplayedLineRange:(HFFPRange)range {
    279 #if ! NDEBUG
    280     HFASSERT(range.location >= 0);
    281     HFASSERT(range.length >= 0);
    282 #endif
    283     if (range.location + range.length > HFULToFP([self totalLineCount])) {
    284         range.location = [self totalLineCount] - range.length;
    285         if (range.location < 0) {
    286             return;
    287         }
    288     }
    289     if (! HFFPRangeEqualsRange(range, displayedLineRange)) {
    290         displayedLineRange = range;
    291         [self _addPropertyChangeBits:HFControllerDisplayedLineRange];
    292     }
    293 }
    294 
    295 - (CGFloat)lineHeight {
    296     return lineHeight;
    297 }
    298 
    299 - (void)setFont:(NSFont *)val {
    300     if (val != _font) {
    301         CGFloat priorLineHeight = [self lineHeight];
    302         
    303         [_font release];
    304         _font = [val copy];
    305         
    306         NSLayoutManager *manager = [[NSLayoutManager alloc] init];
    307         lineHeight = [manager defaultLineHeightForFont:_font];
    308         [manager release];
    309         
    310         HFControllerPropertyBits bits = HFControllerFont;
    311         if (lineHeight != priorLineHeight) bits |= HFControllerLineHeight;
    312         [self _addPropertyChangeBits:bits];
    313         [self _insertPropertyChangeFence];
    314         [self _addPropertyChangeBits:HFControllerViewSizeRatios];
    315         [self _updateDisplayedRange];
    316     }
    317 }
    318 
    319 - (BOOL)shouldAntialias {
    320     return _hfflags.antialias;
    321 }
    322 
    323 - (void)setShouldAntialias:(BOOL)antialias {
    324     antialias = !! antialias;
    325     if (antialias != _hfflags.antialias) {
    326         _hfflags.antialias = antialias;
    327         [self _addPropertyChangeBits:HFControllerAntialias];
    328     }
    329 }
    330 
    331 - (BOOL)shouldColorBytes {
    332     return _hfflags.colorbytes;
    333 }
    334 
    335 - (void)setShouldColorBytes:(BOOL)colorbytes {
    336     colorbytes = !! colorbytes;
    337     if (colorbytes != _hfflags.colorbytes) {
    338         _hfflags.colorbytes = colorbytes;
    339         [self _addPropertyChangeBits:HFControllerColorBytes];
    340     }
    341 }
    342 
    343 - (BOOL)shouldShowCallouts {
    344     return _hfflags.showcallouts;
    345 }
    346 
    347 - (void)setShouldShowCallouts:(BOOL)showcallouts {
    348     showcallouts = !! showcallouts;
    349     if (showcallouts != _hfflags.showcallouts) {
    350         _hfflags.showcallouts = showcallouts;
    351         [self _addPropertyChangeBits:HFControllerShowCallouts];
    352     }
    353 }
    354 
    355 - (BOOL)shouldHideNullBytes {
    356     return _hfflags.hideNullBytes;
    357 }
    358 
    359 - (void)setShouldHideNullBytes:(BOOL)hideNullBytes
    360 {
    361     hideNullBytes = !! hideNullBytes;
    362     if (hideNullBytes != _hfflags.hideNullBytes) {
    363         _hfflags.hideNullBytes = hideNullBytes;
    364         [self _addPropertyChangeBits:HFControllerHideNullBytes];
    365     }
    366 }
    367 
    368 - (BOOL)shouldLiveReload {
    369     return _hfflags.livereload;
    370 }
    371 
    372 - (void)setShouldLiveReload:(BOOL)livereload {
    373     _hfflags.livereload = !!livereload;
    374     
    375 }
    376 
    377 - (void)setBytesPerColumn:(NSUInteger)val {
    378     if (val != bytesPerColumn) {
    379         bytesPerColumn = val;
    380         [self _addPropertyChangeBits:HFControllerBytesPerColumn];
    381     }
    382 }
    383 
    384 - (NSUInteger)bytesPerColumn {
    385     return bytesPerColumn;
    386 }
    387 
    388 - (BOOL)_shouldInvertSelectedRangesByAnchorRange {
    389     return _hfflags.selectionInProgress && _hfflags.commandExtendSelection;
    390 }
    391 
    392 - (NSArray *)_invertedSelectedContentsRanges {
    393     HFASSERT([selectedContentsRanges count] > 0);
    394     HFASSERT(selectionAnchorRange.location != NO_SELECTION);
    395     if (selectionAnchorRange.length == 0) return [NSArray arrayWithArray:selectedContentsRanges];
    396     
    397     NSArray *cleanedRanges = [HFRangeWrapper organizeAndMergeRanges:selectedContentsRanges];
    398     NSMutableArray *result = [NSMutableArray array];
    399     
    400     /* Our algorithm works as follows - add any ranges outside of the selectionAnchorRange, clipped by the selectionAnchorRange.  Then extract every "index" in our cleaned selected arrays that are within the selectionAnchorArray.  An index is the location where a range starts or stops.  Then use those indexes to create the inverted arrays.  A range parity of 1 means that we are adding the range. */
    401     
    402     /* Add all the ranges that are outside of selectionAnchorRange, clipping them if necessary */
    403     HFASSERT(HFSumDoesNotOverflow(selectionAnchorRange.location, selectionAnchorRange.length));
    404     FOREACH(HFRangeWrapper*, outsideWrapper, cleanedRanges) {
    405         HFRange range = [outsideWrapper HFRange];
    406         if (range.location < selectionAnchorRange.location) {
    407             HFRange clippedRange;
    408             clippedRange.location = range.location;
    409             HFASSERT(MIN(HFMaxRange(range), selectionAnchorRange.location) >= clippedRange.location);
    410             clippedRange.length = MIN(HFMaxRange(range), selectionAnchorRange.location) - clippedRange.location;
    411             [result addObject:[HFRangeWrapper withRange:clippedRange]];
    412         }
    413         if (HFMaxRange(range) > HFMaxRange(selectionAnchorRange)) {
    414             HFRange clippedRange;
    415             clippedRange.location = MAX(range.location, HFMaxRange(selectionAnchorRange));
    416             HFASSERT(HFMaxRange(range) >= clippedRange.location);
    417             clippedRange.length = HFMaxRange(range) - clippedRange.location;
    418             [result addObject:[HFRangeWrapper withRange:clippedRange]];
    419         }
    420     }
    421     
    422     HFASSERT(HFSumDoesNotOverflow(selectionAnchorRange.location, selectionAnchorRange.length));
    423     
    424     NEW_ARRAY(unsigned long long, partitions, 2*[cleanedRanges count] + 2);
    425     NSUInteger partitionCount, partitionIndex = 0;
    426     
    427     partitions[partitionIndex++] = selectionAnchorRange.location;
    428     FOREACH(HFRangeWrapper*, wrapper, cleanedRanges) {
    429         HFRange range = [wrapper HFRange];
    430         if (! HFIntersectsRange(range, selectionAnchorRange)) continue;
    431         
    432         partitions[partitionIndex++] = MAX(selectionAnchorRange.location, range.location);
    433         partitions[partitionIndex++] = MIN(HFMaxRange(selectionAnchorRange), HFMaxRange(range));
    434     }
    435     
    436     // For some reason, using HFMaxRange confuses the static analyzer
    437     partitions[partitionIndex++] = HFSum(selectionAnchorRange.location, selectionAnchorRange.length);
    438     
    439     partitionCount = partitionIndex;
    440     HFASSERT((partitionCount % 2) == 0);
    441     
    442     partitionIndex = 0;
    443     while (partitionIndex < partitionCount) {
    444         HFASSERT(partitionIndex + 1 < partitionCount);
    445         HFASSERT(partitions[partitionIndex] <= partitions[partitionIndex + 1]);
    446         if (partitions[partitionIndex] < partitions[partitionIndex + 1]) {
    447             HFRange range = HFRangeMake(partitions[partitionIndex], partitions[partitionIndex + 1] - partitions[partitionIndex]);
    448             [result addObject:[HFRangeWrapper withRange:range]];
    449         }
    450         partitionIndex += 2;
    451     }
    452     
    453     FREE_ARRAY(partitions);
    454     
    455     if ([result count] == 0) [result addObject:[HFRangeWrapper withRange:HFRangeMake(selectionAnchor, 0)]];
    456     
    457     return [HFRangeWrapper organizeAndMergeRanges:result];
    458 }
    459 
    460 - (void)_ensureSelectionIsValid {
    461     HFASSERT(selectedContentsRanges != nil);
    462     HFASSERT([selectedContentsRanges count] > 0);
    463     BOOL onlyOneWrapper = ([selectedContentsRanges count] == 1);
    464     FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
    465         EXPECT_CLASS(wrapper, HFRangeWrapper);
    466         HFRange range = [wrapper HFRange];
    467         if (!HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength]))){
    468             [self setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(0, 0)]]];
    469             return;
    470         }
    471         if (onlyOneWrapper == NO) HFASSERT(range.length > 0); /* If we have more than one wrapper, then none of them should be zero length */
    472     }
    473 }
    474 
    475 - (void)_setSingleSelectedContentsRange:(HFRange)newSelection {
    476     if (!HFRangeIsSubrangeOfRange(newSelection, HFRangeMake(0, [self contentsLength]))) return;
    477     BOOL selectionChanged;
    478     if ([selectedContentsRanges count] == 1) {
    479         selectionChanged = ! HFRangeEqualsRange([selectedContentsRanges[0] HFRange], newSelection);
    480     }
    481     else {
    482         selectionChanged = YES;
    483     }
    484     
    485     if (selectionChanged) {
    486         [selectedContentsRanges removeAllObjects];
    487         [selectedContentsRanges addObject:[HFRangeWrapper withRange:newSelection]];
    488         [self _addPropertyChangeBits:HFControllerSelectedRanges];
    489     }
    490     VALIDATE_SELECTION();
    491 }
    492 
    493 - (NSArray *)selectedContentsRanges {
    494     VALIDATE_SELECTION();
    495     if ([self _shouldInvertSelectedRangesByAnchorRange]) return [self _invertedSelectedContentsRanges];
    496     else return [NSArray arrayWithArray:selectedContentsRanges];
    497 }
    498 
    499 - (unsigned long long)contentsLength {
    500     if (! byteArray) return 0;
    501     else return [byteArray length];
    502 }
    503 
    504 - (NSData *)dataForRange:(HFRange)range {
    505     HFASSERT(range.length <= NSUIntegerMax); // it doesn't make sense to ask for a buffer larger than can be stored in memory
    506     HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
    507     
    508     if(range.length == 0) {
    509         // Don't throw out cache for an empty request! Also makes the analyzer happier.
    510         return [NSData data];
    511     }
    512     
    513     NSUInteger newGenerationIndex = [byteArray changeGenerationCount];
    514     if (cachedData == nil || newGenerationIndex != cachedGenerationIndex || ! HFRangeIsSubrangeOfRange(range, cachedRange)) {
    515         [cachedData release];
    516         cachedGenerationIndex = newGenerationIndex;
    517         cachedRange = range;
    518         NSUInteger length = ll2l(range.length);
    519         unsigned char *data = check_malloc(length);
    520         [byteArray copyBytes:data range:range];
    521         cachedData = [[NSData alloc] initWithBytesNoCopy:data length:length freeWhenDone:YES];
    522     }
    523     
    524     if (HFRangeEqualsRange(range, cachedRange)) {
    525         return cachedData;
    526     }
    527     else {
    528         HFASSERT(cachedRange.location <= range.location);
    529         NSRange cachedDataSubrange;
    530         cachedDataSubrange.location = ll2l(range.location - cachedRange.location);
    531         cachedDataSubrange.length = ll2l(range.length);
    532         return [cachedData subdataWithRange:cachedDataSubrange];
    533     }
    534 }
    535 
    536 - (void)copyBytes:(unsigned char *)bytes range:(HFRange)range {
    537     HFASSERT(range.length <= NSUIntegerMax); // it doesn't make sense to ask for a buffer larger than can be stored in memory
    538     HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
    539     [byteArray copyBytes:bytes range:range];
    540 }
    541 
    542 - (void)_updateDisplayedRange {
    543     HFRange proposedNewDisplayRange;
    544     HFFPRange proposedNewLineRange;
    545     HFRange maxRangeSet = [self _maximumDisplayedRangeSet];
    546     NSUInteger maxBytesForViewSize = NSUIntegerMax;
    547     double maxLines = DBL_MAX;
    548     FOREACH(HFRepresenter*, rep, representers) {
    549         NSView *view = [rep view];
    550         double repMaxLines = [rep maximumAvailableLinesForViewHeight:NSHeight([view frame])];
    551         if (repMaxLines != DBL_MAX) {
    552             /* bytesPerLine may be ULONG_MAX.  We want to compute the smaller of maxBytesForViewSize and ceil(repMaxLines) * bytesPerLine.  If the latter expression overflows, the smaller is the former. */
    553             NSUInteger repMaxLinesUInt = (NSUInteger)ceil(repMaxLines);
    554             NSUInteger maxLinesTimesBytesPerLine =  repMaxLinesUInt * bytesPerLine;
    555             /* Check if we overflowed */
    556             BOOL overflowed = (repMaxLinesUInt != 0 && (maxLinesTimesBytesPerLine / repMaxLinesUInt != bytesPerLine));
    557             if (! overflowed) {
    558                 maxBytesForViewSize = MIN(maxLinesTimesBytesPerLine, maxBytesForViewSize);
    559             }
    560         }
    561         maxLines = MIN(repMaxLines, maxLines);
    562     }
    563     if (maxLines == DBL_MAX) {
    564         proposedNewDisplayRange = HFRangeMake(0, 0);
    565         proposedNewLineRange = (HFFPRange){0, 0};
    566     }
    567     else {
    568         unsigned long long maximumDisplayedBytes = MIN(maxRangeSet.length, maxBytesForViewSize);
    569         HFASSERT(HFMaxRange(maxRangeSet) >= maximumDisplayedBytes);
    570         
    571         proposedNewDisplayRange.location = MIN(HFMaxRange(maxRangeSet) - maximumDisplayedBytes, displayedContentsRange.location);
    572         proposedNewDisplayRange.location -= proposedNewDisplayRange.location % bytesPerLine;
    573         proposedNewDisplayRange.length = MIN(HFMaxRange(maxRangeSet) - proposedNewDisplayRange.location, maxBytesForViewSize);
    574         if (maxBytesForViewSize % bytesPerLine != 0) {
    575             NSLog(@"Bad max bytes: %lu (%lu)", (unsigned long)maxBytesForViewSize, (unsigned long)bytesPerLine);
    576         }
    577         if (HFMaxRange(maxRangeSet) != ULLONG_MAX && (HFMaxRange(maxRangeSet) - proposedNewDisplayRange.location) % bytesPerLine != 0) {
    578             NSLog(@"Bad max range minus: %llu (%lu)", HFMaxRange(maxRangeSet) - proposedNewDisplayRange.location, (unsigned long)bytesPerLine);
    579         }
    580         
    581         long double lastLine = HFULToFP([self totalLineCount]);
    582         proposedNewLineRange.length = MIN(maxLines, lastLine);
    583         proposedNewLineRange.location = MIN(displayedLineRange.location, lastLine - proposedNewLineRange.length);
    584     }
    585     HFASSERT(HFRangeIsSubrangeOfRange(proposedNewDisplayRange, maxRangeSet));
    586     HFASSERT(proposedNewDisplayRange.location % bytesPerLine == 0);
    587     if (! HFRangeEqualsRange(proposedNewDisplayRange, displayedContentsRange) || ! HFFPRangeEqualsRange(proposedNewLineRange, displayedLineRange)) {
    588         displayedContentsRange = proposedNewDisplayRange;
    589         displayedLineRange = proposedNewLineRange;
    590         [self _addPropertyChangeBits:HFControllerDisplayedLineRange];
    591     }
    592 }
    593 
    594 - (void)_ensureVisibilityOfLocation:(unsigned long long)location {
    595     HFASSERT(location <= [self contentsLength]);
    596     unsigned long long lineInt = location / bytesPerLine;
    597     long double line = HFULToFP(lineInt);
    598     HFASSERT(line >= 0);
    599     line  = MIN(line, HFULToFP([self totalLineCount]) - 1);
    600     HFFPRange lineRange = [self displayedLineRange];
    601     HFFPRange newLineRange = lineRange;
    602     if (line < lineRange.location) {
    603         newLineRange.location = line;
    604     }
    605     else if (line >= lineRange.location + lineRange.length) {
    606         HFASSERT(lineRange.location + lineRange.length >= 1);
    607         newLineRange.location = lineRange.location + (line - (lineRange.location + lineRange.length - 1));
    608     }
    609     [self setDisplayedLineRange:newLineRange];
    610 }
    611 
    612 - (void)maximizeVisibilityOfContentsRange:(HFRange)range {
    613     HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
    614     
    615     // Find the minimum move necessary to make range visible
    616     HFFPRange displayRange = [self displayedLineRange];
    617     HFFPRange newDisplayRange = displayRange;
    618     unsigned long long startLine = range.location / bytesPerLine;
    619     unsigned long long endLine = HFDivideULLRoundingUp(HFRoundUpToNextMultipleSaturate(HFMaxRange(range), bytesPerLine), bytesPerLine);
    620     HFASSERT(endLine > startLine || endLine == ULLONG_MAX);
    621     long double linesInRange = HFULToFP(endLine - startLine);
    622     long double linesToDisplay = MIN(displayRange.length, linesInRange);
    623     HFASSERT(linesToDisplay <= linesInRange);
    624     long double linesToMoveDownToMakeLastLineVisible = HFULToFP(endLine) - (displayRange.location + displayRange.length);
    625     long double linesToMoveUpToMakeFirstLineVisible = displayRange.location - HFULToFP(startLine);
    626     //HFASSERT(linesToMoveUpToMakeFirstLineVisible <= 0 || linesToMoveDownToMakeLastLineVisible <= 0);
    627     // in general, we expect either linesToMoveUpToMakeFirstLineVisible to be <= zero, or linesToMoveDownToMakeLastLineVisible to be <= zero.  However, if the available space is smaller than one line, then that won't be true.
    628     if (linesToMoveDownToMakeLastLineVisible > 0) {
    629         newDisplayRange.location += linesToMoveDownToMakeLastLineVisible;
    630     }
    631     else if (linesToMoveUpToMakeFirstLineVisible > 0 && linesToDisplay >= 1) {
    632         // the >= 1 check prevents some wacky behavior when we have less than one line's worth of space, that caused bouncing between the top and bottom of the line
    633         newDisplayRange.location -= linesToMoveUpToMakeFirstLineVisible;
    634     }
    635     
    636     // Use the minimum movement if it would be visually helpful; otherwise just center.
    637     if (HFFPIntersectsRange(displayRange, newDisplayRange)) {
    638         [self setDisplayedLineRange:newDisplayRange];
    639     } else {
    640         [self centerContentsRange:range];
    641     }
    642 }
    643 
    644 - (void)centerContentsRange:(HFRange)range {
    645     HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
    646     HFFPRange displayRange = [self displayedLineRange];
    647     const long double numDisplayedLines = displayRange.length;
    648     HFFPRange newDisplayRange;
    649     unsigned long long startLine = range.location / bytesPerLine;
    650     unsigned long long endLine = HFDivideULLRoundingUp(HFRoundUpToNextMultipleSaturate(HFMaxRange(range), bytesPerLine), bytesPerLine);
    651     HFASSERT(endLine > startLine || endLine == ULLONG_MAX);
    652     long double linesInRange = HFULToFP(endLine - startLine);
    653     
    654     /* Handle the case of a line range bigger than we can display by choosing the top lines. */
    655     if (numDisplayedLines <= linesInRange) {
    656         newDisplayRange = (HFFPRange){startLine, numDisplayedLines};
    657     }
    658     else {
    659         /* Construct a newDisplayRange that centers {startLine, endLine} */
    660         long double center = startLine + (endLine - startLine) / 2.;
    661         newDisplayRange = (HFFPRange){center - numDisplayedLines / 2., numDisplayedLines};
    662     }
    663     
    664     /* Move the newDisplayRange up or down as necessary */
    665     newDisplayRange.location = fmaxl(newDisplayRange.location, (long double)0.);
    666     newDisplayRange.location = fminl(newDisplayRange.location, HFULToFP([self totalLineCount]) - numDisplayedLines);
    667     [self setDisplayedLineRange:newDisplayRange];
    668 }
    669 
    670 /* Clips the selection to a given length.  If this would clip the entire selection, returns a zero length selection at the end.  Indicates HFControllerSelectedRanges if the selection changes. */
    671 - (void)_clipSelectedContentsRangesToLength:(unsigned long long)newLength {
    672     NSMutableArray *newTempSelection = [selectedContentsRanges mutableCopy];
    673     NSUInteger i, max = [newTempSelection count];
    674     for (i=0; i < max; i++) {
    675         HFRange range = [newTempSelection[i] HFRange];
    676         if (HFMaxRange(range) > newLength) {
    677             if (range.location > newLength) {
    678                 /* The range starts past our new max.  Just remove this range entirely */
    679                 [newTempSelection removeObjectAtIndex:i];
    680                 i--;
    681                 max--;
    682             }
    683             else {
    684                 /* Need to clip this range */
    685                 range.length = newLength - range.location;
    686                 newTempSelection[i] = [HFRangeWrapper withRange:range];
    687             }
    688         }
    689     }
    690     [newTempSelection setArray:[HFRangeWrapper organizeAndMergeRanges:newTempSelection]];
    691     
    692     /* If there are multiple empty ranges, remove all but the first */
    693     BOOL foundEmptyRange = NO;
    694     max = [newTempSelection count];
    695     for (i=0; i < max; i++) {
    696         HFRange range = [newTempSelection[i] HFRange];
    697         HFASSERT(HFMaxRange(range) <= newLength);
    698         if (range.length == 0) {
    699             if (foundEmptyRange) {
    700                 [newTempSelection removeObjectAtIndex:i];
    701                 i--;
    702                 max--;
    703             }
    704             foundEmptyRange = YES;
    705         }
    706     }
    707     if (max == 0) {
    708         /* Removed all ranges - insert one at the end */
    709         [newTempSelection addObject:[HFRangeWrapper withRange:HFRangeMake(newLength, 0)]];
    710     }
    711     
    712     /* If something changed, set the new selection and post the change bit */
    713     if (! [selectedContentsRanges isEqualToArray:newTempSelection]) {
    714         [selectedContentsRanges setArray:newTempSelection];
    715         [self _addPropertyChangeBits:HFControllerSelectedRanges];
    716     }
    717     
    718     [newTempSelection release];
    719 }
    720 
    721 - (void)setByteArray:(HFByteArray *)val {
    722     REQUIRE_NOT_NULL(val);
    723     BEGIN_TRANSACTION();
    724     [byteArray removeObserver:self forKeyPath:@"changesAreLocked"];
    725     [val retain];
    726     [byteArray release];
    727     byteArray = val;
    728     [cachedData release];
    729     cachedData = nil;
    730     [byteArray addObserver:self forKeyPath:@"changesAreLocked" options:0 context:KVOContextChangesAreLocked];
    731     [self _updateDisplayedRange];
    732     [self _addPropertyChangeBits: HFControllerContentValue | HFControllerContentLength];
    733     [self _clipSelectedContentsRangesToLength:[byteArray length]];
    734     END_TRANSACTION();
    735 }
    736 
    737 - (HFByteArray *)byteArray {
    738     return byteArray;
    739 }
    740 
    741 - (void)_undoNotification:note {
    742     USE(note);
    743 }
    744 
    745 - (void)_removeUndoManagerNotifications {
    746     if (undoManager) {
    747         NSNotificationCenter *noter = [NSNotificationCenter defaultCenter];
    748         [noter removeObserver:self name:NSUndoManagerWillUndoChangeNotification object:undoManager];
    749     }
    750 }
    751 
    752 - (void)_addUndoManagerNotifications {
    753     if (undoManager) {
    754         NSNotificationCenter *noter = [NSNotificationCenter defaultCenter];
    755         [noter addObserver:self selector:@selector(_undoNotification:) name:NSUndoManagerWillUndoChangeNotification object:undoManager];
    756     }
    757 }
    758 
    759 - (void)_removeAllUndoOperations {
    760     /* Remove all the undo operations, because some undo operation is unsupported. Note that if we were smarter we would keep a stack of undo operations and only remove ones "up to" a certain point. */
    761     [undoManager removeAllActionsWithTarget:self];
    762     [undoOperations makeObjectsPerformSelector:@selector(invalidate)];
    763     [undoOperations removeAllObjects];
    764 }
    765 
    766 - (void)setUndoManager:(NSUndoManager *)manager {
    767     [self _removeUndoManagerNotifications];
    768     [self _removeAllUndoOperations];
    769     [manager retain];
    770     [undoManager release];
    771     undoManager = manager;
    772     [self _addUndoManagerNotifications];
    773 }
    774 
    775 - (NSUndoManager *)undoManager {
    776     return undoManager;
    777 }
    778 
    779 - (NSUInteger)bytesPerLine {
    780     return bytesPerLine;
    781 }
    782 
    783 - (BOOL)editable {
    784     return _hfflags.editable && ! [byteArray changesAreLocked] && _hfflags.editMode != HFReadOnlyMode;
    785 }
    786 
    787 - (void)setEditable:(BOOL)flag {
    788     if (flag != _hfflags.editable) {
    789         _hfflags.editable = flag;
    790         [self _addPropertyChangeBits:HFControllerEditable];
    791     }
    792 }
    793 
    794 - (void)_updateBytesPerLine {
    795     NSUInteger newBytesPerLine = NSUIntegerMax;
    796     FOREACH(HFRepresenter*, rep, representers) {
    797         NSView *view = [rep view];
    798         CGFloat width = [view frame].size.width;
    799         NSUInteger repMaxBytesPerLine = [rep maximumBytesPerLineForViewWidth:width];
    800         HFASSERT(repMaxBytesPerLine > 0);
    801         newBytesPerLine = MIN(repMaxBytesPerLine, newBytesPerLine);
    802     }
    803     if (newBytesPerLine != bytesPerLine) {
    804         HFASSERT(newBytesPerLine > 0);
    805         bytesPerLine = newBytesPerLine;
    806         BEGIN_TRANSACTION();
    807         [self _addPropertyChangeBits:HFControllerBytesPerLine];
    808         END_TRANSACTION();
    809     }
    810 }
    811 
    812 - (void)representer:(HFRepresenter *)rep changedProperties:(HFControllerPropertyBits)properties {
    813     USE(rep);
    814     HFControllerPropertyBits remainingProperties = properties;
    815     BEGIN_TRANSACTION();
    816     if (remainingProperties & HFControllerBytesPerLine) {
    817         [self _updateBytesPerLine];
    818         remainingProperties &= ~HFControllerBytesPerLine;
    819     }
    820     if (remainingProperties & HFControllerDisplayedLineRange) {
    821         [self _updateDisplayedRange];
    822         remainingProperties &= ~HFControllerDisplayedLineRange;
    823     }
    824     if (remainingProperties & HFControllerByteRangeAttributes) {
    825         [self _addPropertyChangeBits:HFControllerByteRangeAttributes];
    826         remainingProperties &= ~HFControllerByteRangeAttributes;
    827     }
    828     if (remainingProperties & HFControllerViewSizeRatios) {
    829         [self _addPropertyChangeBits:HFControllerViewSizeRatios];
    830         remainingProperties &= ~HFControllerViewSizeRatios;
    831     }
    832     if (remainingProperties) {
    833         NSLog(@"Unknown properties: %lx", (long)remainingProperties);
    834     }
    835     END_TRANSACTION();
    836 }
    837 
    838 - (HFByteArray *)byteArrayForSelectedContentsRanges {
    839     HFByteArray *result = nil;
    840     HFByteArray *bytes = [self byteArray];
    841     VALIDATE_SELECTION();
    842     FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
    843         HFRange range = [wrapper HFRange];
    844         HFByteArray *additionalBytes = [bytes subarrayWithRange:range];
    845         if (! result) {
    846             result = additionalBytes;
    847         }
    848         else {
    849             [result insertByteArray:additionalBytes inRange:HFRangeMake([result length], 0)];
    850         }
    851     }
    852     return result;
    853 }
    854 
    855 /* Flattens the selected range to a single range (the selected range becomes any character within or between the selected ranges).  Modifies the selectedContentsRanges and returns the new single HFRange.  Does not call notifyRepresentersOfChanges: */
    856 - (HFRange)_flattenSelectionRange {
    857     HFASSERT([selectedContentsRanges count] >= 1);
    858     
    859     HFRange resultRange = [selectedContentsRanges[0] HFRange];
    860     if ([selectedContentsRanges count] == 1) return resultRange; //already flat
    861     
    862     FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
    863         HFRange selectedRange = [wrapper HFRange];
    864         if (selectedRange.location < resultRange.location) {
    865             /* Extend our result range backwards */
    866             resultRange.length += resultRange.location - selectedRange.location;
    867             resultRange.location = selectedRange.location;
    868         }
    869         if (HFRangeExtendsPastRange(selectedRange, resultRange)) {
    870             HFASSERT(selectedRange.location >= resultRange.location); //must be true by if statement above
    871             resultRange.length = HFSum(selectedRange.location - resultRange.location, selectedRange.length);
    872         }
    873     }
    874     [self _setSingleSelectedContentsRange:resultRange];
    875     return resultRange;
    876 }
    877 
    878 - (unsigned long long)_minimumSelectionLocation {
    879     HFASSERT([selectedContentsRanges count] >= 1);
    880     unsigned long long minSelection = ULLONG_MAX;
    881     FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
    882         HFRange range = [wrapper HFRange];
    883         minSelection = MIN(minSelection, range.location);
    884     }
    885     return minSelection;
    886 }
    887 
    888 - (unsigned long long)_maximumSelectionLocation {
    889     HFASSERT([selectedContentsRanges count] >= 1);
    890     unsigned long long maxSelection = 0;
    891     FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
    892         HFRange range = [wrapper HFRange];
    893         maxSelection = MAX(maxSelection, HFMaxRange(range));
    894     }
    895     return maxSelection;
    896 }
    897 
    898 - (unsigned long long)minimumSelectionLocation {
    899     return [self _minimumSelectionLocation];
    900 }
    901 
    902 - (unsigned long long)maximumSelectionLocation {
    903     return [self _maximumSelectionLocation];
    904 }
    905 
    906 /* Put the selection at the left or right end of the current selection, with zero length.  Modifies the selectedContentsRanges and returns the new single HFRange.  Does not call notifyRepresentersOfChanges: */
    907 - (HFRange)_telescopeSelectionRangeInDirection:(HFControllerMovementDirection)direction {
    908     HFRange resultRange;
    909     HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
    910     resultRange.location = (direction == HFControllerDirectionLeft ? [self _minimumSelectionLocation] : [self _maximumSelectionLocation]);
    911     resultRange.length = 0;
    912     [self _setSingleSelectedContentsRange:resultRange];
    913     return resultRange;
    914 }
    915 
    916 - (void)beginSelectionWithEvent:(NSEvent *)event forByteIndex:(unsigned long long)characterIndex {
    917     USE(event);
    918     HFASSERT(characterIndex <= [self contentsLength]);
    919     
    920     /* Determine how to perform the selection - normally, with command key, or with shift key.  Command + shift is the same as command. The shift key closes the selection - the selected range becomes the single range containing the first and last selected character. */
    921     _hfflags.shiftExtendSelection = NO;
    922     _hfflags.commandExtendSelection = NO;
    923     NSUInteger flags = [event modifierFlags];
    924     if (flags & NSCommandKeyMask) _hfflags.commandExtendSelection = YES;
    925     else if (flags & NSShiftKeyMask) _hfflags.shiftExtendSelection = YES;
    926     
    927     selectionAnchor = NO_SELECTION;
    928     selectionAnchorRange = HFRangeMake(NO_SELECTION, 0);
    929     
    930     _hfflags.selectionInProgress = YES;
    931     if (_hfflags.commandExtendSelection) {
    932         /* The selection anchor is used to track the "invert" range.  All characters within this range have their selection inverted.  This is tracked by the _shouldInvertSelectedRangesByAnchorRange method. */
    933         selectionAnchor = characterIndex;
    934         selectionAnchorRange = HFRangeMake(characterIndex, 0);
    935     }
    936     else if (_hfflags.shiftExtendSelection) {
    937         /* The selection anchor is used to track the single (flattened) selected range. */
    938         HFRange selectedRange = [self _flattenSelectionRange];
    939         unsigned long long distanceFromRangeStart = HFAbsoluteDifference(selectedRange.location, characterIndex);
    940         unsigned long long distanceFromRangeEnd = HFAbsoluteDifference(HFMaxRange(selectedRange), characterIndex);
    941         if (selectedRange.length == 0) {    
    942             HFASSERT(distanceFromRangeStart == distanceFromRangeEnd);
    943             selectionAnchor = selectedRange.location;
    944             selectedRange.location = MIN(characterIndex, selectedRange.location);
    945             selectedRange.length = distanceFromRangeStart;
    946         }
    947         else if (distanceFromRangeStart >= distanceFromRangeEnd) {
    948             /* Push the "end forwards" */
    949             selectedRange.length = distanceFromRangeStart;
    950             selectionAnchor = selectedRange.location;
    951         }
    952         else {
    953             /* Push the "start back" */
    954             selectedRange.location = selectedRange.location + selectedRange.length - distanceFromRangeEnd;
    955             selectedRange.length = distanceFromRangeEnd;
    956             selectionAnchor = HFSum(selectedRange.length, selectedRange.location);
    957         }
    958         HFASSERT(HFRangeIsSubrangeOfRange(selectedRange, HFRangeMake(0, [self contentsLength])));
    959         selectionAnchorRange = selectedRange;
    960         [self _setSingleSelectedContentsRange:selectedRange];
    961     }
    962     else {
    963         /* No modifier key selection.  The selection anchor is not used.  */
    964         [self _setSingleSelectedContentsRange:HFRangeMake(characterIndex, 0)];
    965         selectionAnchor = characterIndex;
    966     }
    967 }
    968 
    969 - (void)continueSelectionWithEvent:(NSEvent *)event forByteIndex:(unsigned long long)byteIndex {
    970     USE(event);
    971     HFASSERT(_hfflags.selectionInProgress);
    972     HFASSERT(byteIndex <= [self contentsLength]);
    973     BEGIN_TRANSACTION();
    974     if (_hfflags.commandExtendSelection) {
    975         /* Clear any zero-length ranges, unless there's only one */
    976         NSUInteger rangeCount = [selectedContentsRanges count];
    977         NSUInteger rangeIndex = rangeCount;
    978         while (rangeIndex-- > 0) {
    979             if (rangeCount > 1 && [selectedContentsRanges[rangeIndex] HFRange].length == 0) {
    980                 [selectedContentsRanges removeObjectAtIndex:rangeIndex];
    981                 rangeCount--;
    982             }
    983         }
    984         selectionAnchorRange.location = MIN(byteIndex, selectionAnchor);
    985         selectionAnchorRange.length = MAX(byteIndex, selectionAnchor) - selectionAnchorRange.location;
    986         [self _addPropertyChangeBits:HFControllerSelectedRanges];
    987     }
    988     else if (_hfflags.shiftExtendSelection) {
    989         HFASSERT(selectionAnchorRange.location != NO_SELECTION);
    990         HFASSERT(selectionAnchor != NO_SELECTION);
    991         HFRange range;
    992         if (! HFLocationInRange(byteIndex, selectionAnchorRange)) {
    993             /* The character index is outside of the selection anchor range.  The new range is just the selected anchor range combined with the character index. */
    994             range.location = MIN(byteIndex, selectionAnchorRange.location);
    995             unsigned long long rangeEnd = MAX(byteIndex, HFSum(selectionAnchorRange.location, selectionAnchorRange.length));
    996             HFASSERT(rangeEnd >= range.location);
    997             range.length = rangeEnd - range.location;
    998         }
    999         else {
   1000             /* The character is within the selection anchor range.  We use the selection anchor index to determine which "side" of the range is selected. */
   1001             range.location = MIN(selectionAnchor, byteIndex);
   1002             range.length = HFAbsoluteDifference(selectionAnchor, byteIndex);
   1003         }
   1004         [self _setSingleSelectedContentsRange:range];
   1005     }
   1006     else {
   1007         /* No modifier key selection */
   1008         HFRange range;
   1009         range.location = MIN(byteIndex, selectionAnchor);
   1010         range.length = MAX(byteIndex, selectionAnchor) - range.location;
   1011         [self _setSingleSelectedContentsRange:range];
   1012     }
   1013     END_TRANSACTION();
   1014     VALIDATE_SELECTION();
   1015 }
   1016 
   1017 - (void)endSelectionWithEvent:(NSEvent *)event forByteIndex:(unsigned long long)characterIndex {
   1018     USE(event);
   1019     HFASSERT(_hfflags.selectionInProgress);
   1020     HFASSERT(characterIndex <= [self contentsLength]);
   1021     if (_hfflags.commandExtendSelection) {
   1022         selectionAnchorRange.location = MIN(characterIndex, selectionAnchor);
   1023         selectionAnchorRange.length = MAX(characterIndex, selectionAnchor) - selectionAnchorRange.location;
   1024         
   1025         /* "Commit" our selectionAnchorRange */
   1026         NSArray *newSelection = [self _invertedSelectedContentsRanges];
   1027         [selectedContentsRanges setArray:newSelection];
   1028     }
   1029     else if (_hfflags.shiftExtendSelection) {
   1030         HFASSERT(selectionAnchorRange.location != NO_SELECTION);
   1031         HFASSERT(selectionAnchor != NO_SELECTION);
   1032         HFRange range;
   1033         if (! HFLocationInRange(characterIndex, selectionAnchorRange)) {
   1034             /* The character index is outside of the selection anchor range.  The new range is just the selected anchor range combined with the character index. */
   1035             range.location = MIN(characterIndex, selectionAnchorRange.location);
   1036             unsigned long long rangeEnd = MAX(characterIndex, HFSum(selectionAnchorRange.location, selectionAnchorRange.length));
   1037             HFASSERT(rangeEnd >= range.location);
   1038             range.length = rangeEnd - range.location;
   1039         }
   1040         else {
   1041             /* The character is within the selection anchor range.  We use the selection anchor index to determine which "side" of the range is selected. */
   1042             range.location = MIN(selectionAnchor, characterIndex);
   1043             range.length = HFAbsoluteDifference(selectionAnchor, characterIndex);
   1044         }
   1045         [self _setSingleSelectedContentsRange:range];
   1046     }
   1047     else {
   1048         /* No modifier key selection */
   1049         HFRange range;
   1050         range.location = MIN(characterIndex, selectionAnchor);
   1051         range.length = MAX(characterIndex, selectionAnchor) - range.location;
   1052         [self _setSingleSelectedContentsRange:range];
   1053     }
   1054     
   1055     _hfflags.selectionInProgress = NO;    
   1056     _hfflags.shiftExtendSelection = NO;
   1057     _hfflags.commandExtendSelection = NO;
   1058     selectionAnchor = NO_SELECTION;
   1059 }
   1060 
   1061 - (double)selectionPulseAmount {
   1062     double result = 0;
   1063     if (pulseSelectionStartTime > 0) {
   1064         CFTimeInterval diff = pulseSelectionCurrentTime - pulseSelectionStartTime;
   1065         if (diff > 0 && diff < kPulseDuration) {
   1066             result = 1. - fabs(diff * 2 - kPulseDuration) / kPulseDuration;
   1067         }
   1068     }
   1069     return result;
   1070 }
   1071 
   1072 - (void)firePulseTimer:(NSTimer *)timer {
   1073     USE(timer);
   1074     HFASSERT(pulseSelectionStartTime != 0);
   1075     pulseSelectionCurrentTime = CFAbsoluteTimeGetCurrent();
   1076     [self _addPropertyChangeBits:HFControllerSelectionPulseAmount];
   1077     if (pulseSelectionCurrentTime - pulseSelectionStartTime > kPulseDuration) {
   1078         [pulseSelectionTimer invalidate];
   1079         [pulseSelectionTimer release];
   1080         pulseSelectionTimer = nil;
   1081     }
   1082 }
   1083 
   1084 - (void)pulseSelection {
   1085     pulseSelectionStartTime = CFAbsoluteTimeGetCurrent();
   1086     if (pulseSelectionTimer == nil) {
   1087         pulseSelectionTimer = [[NSTimer scheduledTimerWithTimeInterval:(1. / 30.) target:self selector:@selector(firePulseTimer:) userInfo:nil repeats:YES] retain];
   1088     }
   1089 }
   1090 
   1091 - (void)scrollByLines:(long double)lines {
   1092     HFFPRange lineRange = [self displayedLineRange];
   1093     HFASSERT(HFULToFP([self totalLineCount]) >= lineRange.length);
   1094     long double maxScroll = HFULToFP([self totalLineCount]) - lineRange.length;
   1095     if (lines < 0) {
   1096         lineRange.location -= MIN(lineRange.location, -lines);
   1097     }
   1098     else {
   1099         lineRange.location = MIN(maxScroll, lineRange.location + lines);
   1100     }
   1101     [self setDisplayedLineRange:lineRange];
   1102 }
   1103 
   1104 - (void)scrollWithScrollEvent:(NSEvent *)scrollEvent {
   1105     HFASSERT(scrollEvent != NULL);
   1106     HFASSERT([scrollEvent type] == NSScrollWheel);
   1107     CGFloat preciseScroll = 0;
   1108     BOOL hasPreciseScroll;
   1109     
   1110     /* Prefer precise deltas */
   1111     if ([scrollEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)]) {
   1112         hasPreciseScroll = [scrollEvent hasPreciseScrollingDeltas];
   1113         if (hasPreciseScroll) {
   1114             /* In this case, we're going to scroll by a certain number of points */
   1115             preciseScroll = [scrollEvent scrollingDeltaY];
   1116         }
   1117     } else if ([scrollEvent respondsToSelector:@selector(deviceDeltaY)]) {
   1118         /* Legacy (SnowLeopard) support */
   1119         hasPreciseScroll = ([scrollEvent subtype] == 1);
   1120         if (hasPreciseScroll) {
   1121             preciseScroll = [scrollEvent deviceDeltaY];
   1122         }
   1123     } else {
   1124         hasPreciseScroll = NO;
   1125     }
   1126     
   1127     long double scrollY = 0;
   1128     if (! hasPreciseScroll) {
   1129         scrollY = -kScrollMultiplier * [scrollEvent deltaY];
   1130     } else {
   1131         scrollY = -preciseScroll / [self lineHeight];
   1132     }
   1133     [self scrollByLines:scrollY];
   1134 }
   1135 
   1136 - (void)setSelectedContentsRanges:(NSArray *)selectedRanges {
   1137     REQUIRE_NOT_NULL(selectedRanges);
   1138     [selectedContentsRanges setArray:selectedRanges];
   1139     VALIDATE_SELECTION();
   1140     selectionAnchor = NO_SELECTION;
   1141     [self _addPropertyChangeBits:HFControllerSelectedRanges];
   1142 }
   1143 
   1144 - (IBAction)selectAll:sender {
   1145     USE(sender);
   1146     if (_hfflags.selectable) {
   1147         [self _setSingleSelectedContentsRange:HFRangeMake(0, [self contentsLength])];
   1148     }
   1149 }
   1150 
   1151 - (void)_addRangeToSelection:(HFRange)range {
   1152     [selectedContentsRanges addObject:[HFRangeWrapper withRange:range]];
   1153     [selectedContentsRanges setArray:[HFRangeWrapper organizeAndMergeRanges:selectedContentsRanges]];
   1154     VALIDATE_SELECTION();
   1155 }
   1156 
   1157 - (void)_removeRangeFromSelection:(HFRange)inputRange withCursorLocationIfAllSelectionRemoved:(unsigned long long)cursorLocation {
   1158     NSUInteger selectionCount = [selectedContentsRanges count];
   1159     HFASSERT(selectionCount > 0 && selectionCount <= NSUIntegerMax / 2);
   1160     NSUInteger rangeIndex = 0;
   1161     NSArray *wrappers;
   1162     NEW_ARRAY(HFRange, tempRanges, selectionCount * 2);
   1163     FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
   1164         HFRange range = [wrapper HFRange];
   1165         if (! HFIntersectsRange(range, inputRange)) {
   1166             tempRanges[rangeIndex++] = range;
   1167         }
   1168         else {
   1169             if (range.location < inputRange.location) {
   1170                 tempRanges[rangeIndex++] = HFRangeMake(range.location, inputRange.location - range.location);
   1171             }
   1172             if (HFMaxRange(range) > HFMaxRange(inputRange)) {
   1173                 tempRanges[rangeIndex++] = HFRangeMake(HFMaxRange(inputRange), HFMaxRange(range) - HFMaxRange(inputRange));
   1174             }
   1175         }
   1176     }
   1177     if (rangeIndex == 0 || (rangeIndex == 1 && tempRanges[0].length == 0)) {
   1178         /* We removed all of our ranges.  Telescope us. */
   1179         HFASSERT(cursorLocation <= [self contentsLength]);
   1180         [self _setSingleSelectedContentsRange:HFRangeMake(cursorLocation, 0)];
   1181     }
   1182     else {
   1183         wrappers = [HFRangeWrapper withRanges:tempRanges count:rangeIndex];
   1184         [selectedContentsRanges setArray:[HFRangeWrapper organizeAndMergeRanges:wrappers]];
   1185     }
   1186     FREE_ARRAY(tempRanges);
   1187     VALIDATE_SELECTION();    
   1188 }
   1189 
   1190 - (void)_moveDirectionDiscardingSelection:(HFControllerMovementDirection)direction byAmount:(unsigned long long)amountToMove {
   1191     HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
   1192     BEGIN_TRANSACTION();
   1193     BOOL selectionWasEmpty = ([selectedContentsRanges count] == 1 && [selectedContentsRanges[0] HFRange].length == 0);
   1194     BOOL directionIsForward = (direction == HFControllerDirectionRight);
   1195     HFRange selectedRange = [self _telescopeSelectionRangeInDirection: (directionIsForward ? HFControllerDirectionRight : HFControllerDirectionLeft)];
   1196     HFASSERT(selectedRange.length == 0);
   1197     HFASSERT([self contentsLength] >= selectedRange.location);
   1198     /* A movement of just 1 with a selection only clears the selection; it does not move the cursor */
   1199     if (selectionWasEmpty || amountToMove > 1) {
   1200         if (direction == HFControllerDirectionLeft) {
   1201             selectedRange.location -= MIN(amountToMove, selectedRange.location);
   1202         }
   1203         else {
   1204             selectedRange.location += MIN(amountToMove, [self contentsLength] - selectedRange.location);
   1205         }
   1206     }
   1207     selectionAnchor = NO_SELECTION;
   1208     [self _setSingleSelectedContentsRange:selectedRange];
   1209     [self _ensureVisibilityOfLocation:selectedRange.location];
   1210     END_TRANSACTION();
   1211 }
   1212 
   1213 /* In _extendSelectionInDirection:byAmount:, we only allow left/right movement.  up/down is not allowed. */
   1214 - (void)_extendSelectionInDirection:(HFControllerMovementDirection)direction byAmount:(unsigned long long)amountToMove {
   1215     HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
   1216     unsigned long long minSelection = [self _minimumSelectionLocation];
   1217     unsigned long long maxSelection = [self _maximumSelectionLocation];
   1218     BOOL selectionChanged = NO;
   1219     unsigned long long locationToMakeVisible = NO_SELECTION;
   1220     unsigned long long contentsLength = [self contentsLength];
   1221     if (selectionAnchor == NO_SELECTION) {
   1222         /* Pick the anchor opposite the choice of direction */
   1223         if (direction == HFControllerDirectionLeft) selectionAnchor = maxSelection;
   1224         else selectionAnchor = minSelection;
   1225     }
   1226     if (direction == HFControllerDirectionLeft) {
   1227         if (minSelection >= selectionAnchor && maxSelection > minSelection) {
   1228             unsigned long long amountToRemove = llmin(maxSelection - selectionAnchor, amountToMove);
   1229             unsigned long long amountToAdd = llmin(amountToMove - amountToRemove, selectionAnchor);
   1230             if (amountToRemove > 0) [self _removeRangeFromSelection:HFRangeMake(maxSelection - amountToRemove, amountToRemove) withCursorLocationIfAllSelectionRemoved:minSelection];
   1231             if (amountToAdd > 0) [self _addRangeToSelection:HFRangeMake(selectionAnchor - amountToAdd, amountToAdd)];
   1232             selectionChanged = YES;
   1233             locationToMakeVisible = (amountToAdd > 0 ? selectionAnchor - amountToAdd : maxSelection - amountToRemove);
   1234         }
   1235         else {
   1236             if (minSelection > 0) {
   1237                 NSUInteger amountToAdd = ll2l(llmin(minSelection, amountToMove));
   1238                 if (amountToAdd > 0) [self _addRangeToSelection:HFRangeMake(minSelection - amountToAdd, amountToAdd)];
   1239                 selectionChanged = YES;
   1240                 locationToMakeVisible = minSelection - amountToAdd;
   1241             }
   1242         }
   1243     }
   1244     else if (direction == HFControllerDirectionRight) {
   1245         if (maxSelection <= selectionAnchor && maxSelection > minSelection) {
   1246             HFASSERT(contentsLength >= maxSelection);
   1247             unsigned long long amountToRemove = ll2l(llmin(maxSelection - minSelection, amountToMove));
   1248             unsigned long long amountToAdd = amountToMove - amountToRemove;
   1249             if (amountToRemove > 0) [self _removeRangeFromSelection:HFRangeMake(minSelection, amountToRemove) withCursorLocationIfAllSelectionRemoved:maxSelection];
   1250             if (amountToAdd > 0) [self _addRangeToSelection:HFRangeMake(maxSelection, amountToAdd)];
   1251             selectionChanged = YES;
   1252             locationToMakeVisible = llmin(contentsLength, (amountToAdd > 0 ? maxSelection + amountToAdd : minSelection + amountToRemove));
   1253         }
   1254         else {
   1255             if (maxSelection < contentsLength) {
   1256                 NSUInteger amountToAdd = ll2l(llmin(contentsLength - maxSelection, amountToMove));
   1257                 [self _addRangeToSelection:HFRangeMake(maxSelection, amountToAdd)];
   1258                 selectionChanged = YES;
   1259                 locationToMakeVisible = maxSelection + amountToAdd;
   1260             }
   1261         }
   1262     }
   1263     if (selectionChanged) {
   1264         BEGIN_TRANSACTION();
   1265         [self _addPropertyChangeBits:HFControllerSelectedRanges];
   1266         if (locationToMakeVisible != NO_SELECTION) [self _ensureVisibilityOfLocation:locationToMakeVisible];
   1267         END_TRANSACTION();
   1268     }
   1269 }
   1270 
   1271 /* Returns the distance to the next "word" (at least 1, unless we are empty).  Here a word is identified as a column.  If there are no columns, a word is a line.  This is used for word movement (e.g. option + right arrow) */
   1272 - (unsigned long long)_distanceToWordBoundaryForDirection:(HFControllerMovementDirection)direction {
   1273     unsigned long long result = 0, locationToConsider;
   1274     
   1275     /* Figure out how big a word is.  By default, it's the column width, unless we have no columns, in which case it's the bytes per line. */
   1276     NSUInteger wordGranularity = [self bytesPerColumn];
   1277     if (wordGranularity == 0) wordGranularity = MAX(1u, [self bytesPerLine]);
   1278     if (selectionAnchor == NO_SELECTION) {
   1279         /* Pick the anchor inline with the choice of direction */
   1280         if (direction == HFControllerDirectionLeft) locationToConsider = [self _minimumSelectionLocation];
   1281         else locationToConsider = [self _maximumSelectionLocation];
   1282     } else {
   1283         /* Just use the anchor */
   1284         locationToConsider = selectionAnchor;
   1285     }
   1286     if (direction == HFControllerDirectionRight) {
   1287         result = HFRoundUpToNextMultipleSaturate(locationToConsider, wordGranularity) - locationToConsider;
   1288     } else {
   1289         result = locationToConsider % wordGranularity;
   1290         if (result == 0) result = wordGranularity;
   1291     }
   1292     return result;
   1293     
   1294 }
   1295 
   1296 /* Anchored selection is not allowed; neither is up/down movement */
   1297 - (void)_shiftSelectionInDirection:(HFControllerMovementDirection)direction byAmount:(unsigned long long)amountToMove {
   1298     HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
   1299     HFASSERT(selectionAnchor == NO_SELECTION);
   1300     NSUInteger i, max = [selectedContentsRanges count];
   1301     const unsigned long long maxLength = [self contentsLength];
   1302     NSMutableArray *newRanges = [NSMutableArray arrayWithCapacity:max];
   1303     BOOL hasAddedNonemptyRange = NO;
   1304     for (i=0; i < max; i++) {
   1305         HFRange range = [selectedContentsRanges[i] HFRange];
   1306         HFASSERT(range.location <= maxLength && HFMaxRange(range) <= maxLength);
   1307         if (direction == HFControllerDirectionRight) {
   1308             unsigned long long offset = MIN(maxLength - range.location, amountToMove);
   1309             unsigned long long lengthToSubtract = MIN(range.length, amountToMove - offset);
   1310             range.location += offset;
   1311             range.length -= lengthToSubtract;
   1312         }
   1313         else { /* direction == HFControllerDirectionLeft */
   1314             unsigned long long negOffset = MIN(amountToMove, range.location);
   1315             unsigned long long lengthToSubtract = MIN(range.length, amountToMove - negOffset);
   1316             range.location -= negOffset;
   1317             range.length -= lengthToSubtract;
   1318         }
   1319         [newRanges addObject:[HFRangeWrapper withRange:range]];
   1320         hasAddedNonemptyRange = hasAddedNonemptyRange || (range.length > 0);
   1321     }
   1322     
   1323     newRanges = [[[HFRangeWrapper organizeAndMergeRanges:newRanges] mutableCopy] autorelease];
   1324     
   1325     BOOL hasFoundEmptyRange = NO;
   1326     max = [newRanges count];
   1327     for (i=0; i < max; i++) {
   1328         HFRange range = [newRanges[i] HFRange];
   1329         if (range.length == 0) {
   1330             if (hasFoundEmptyRange || hasAddedNonemptyRange) {
   1331                 [newRanges removeObjectAtIndex:i];
   1332                 i--;
   1333                 max--;
   1334             }
   1335             hasFoundEmptyRange = YES;
   1336         }
   1337     }
   1338     [selectedContentsRanges setArray:newRanges];
   1339     VALIDATE_SELECTION();
   1340     [self _addPropertyChangeBits:HFControllerSelectedRanges];
   1341 }
   1342 
   1343 __attribute__((unused))
   1344 static BOOL rangesAreInAscendingOrder(NSEnumerator *rangeEnumerator) {
   1345     unsigned long long index = 0;
   1346     HFRangeWrapper *rangeWrapper;
   1347     while ((rangeWrapper = [rangeEnumerator nextObject])) {
   1348         HFRange range = [rangeWrapper HFRange];
   1349         if (range.location < index) return NO;
   1350         index = HFSum(range.location, range.length);
   1351     }
   1352     return YES;
   1353 }
   1354 
   1355 - (BOOL)_registerCondemnedRangesForUndo:(NSArray *)ranges selectingRangesAfterUndo:(BOOL)selectAfterUndo {
   1356     HFASSERT(ranges != NULL);
   1357     HFASSERT(ranges != selectedContentsRanges); //selectedContentsRanges is mutable - we really don't want to stash it away with undo
   1358     BOOL result = NO;
   1359     NSUndoManager *manager = [self undoManager];
   1360     NSUInteger rangeCount = [ranges count];
   1361     if (! manager || ! rangeCount) return NO;
   1362     
   1363     HFASSERT(rangesAreInAscendingOrder([ranges objectEnumerator]));
   1364     
   1365     NSMutableArray *rangesToRestore = [NSMutableArray arrayWithCapacity:rangeCount];
   1366     NSMutableArray *correspondingByteArrays = [NSMutableArray arrayWithCapacity:rangeCount];
   1367     HFByteArray *bytes = [self byteArray];
   1368     
   1369     /* Enumerate the ranges in forward order so when we insert them, we insert later ranges before earlier ones, so we don't have to worry about shifting indexes */
   1370     FOREACH(HFRangeWrapper *, rangeWrapper, ranges) {
   1371         HFRange range = [rangeWrapper HFRange];
   1372         if (range.length > 0) {
   1373             [rangesToRestore addObject:[HFRangeWrapper withRange:HFRangeMake(range.location, 0)]];
   1374             [correspondingByteArrays addObject:[bytes subarrayWithRange:range]];
   1375             result = YES;
   1376         }
   1377     }
   1378     
   1379     if (result) [self _registerUndoOperationForInsertingByteArrays:correspondingByteArrays inRanges:rangesToRestore withSelectionAction:(selectAfterUndo ? eSelectResult : eSelectAfterResult)];    
   1380     return result;
   1381 }
   1382 
   1383 - (void)_commandDeleteRanges:(NSArray *)rangesToDelete {
   1384     HFASSERT(rangesToDelete != selectedContentsRanges); //selectedContentsRanges is mutable - we really don't want to stash it away with undo
   1385     HFASSERT(rangesAreInAscendingOrder([rangesToDelete objectEnumerator]));
   1386     
   1387     /* Delete all the selection - in reverse order */
   1388     unsigned long long minSelection = ULLONG_MAX;
   1389     BOOL somethingWasDeleted = NO;
   1390     [self _registerCondemnedRangesForUndo:rangesToDelete selectingRangesAfterUndo:YES];
   1391     NSUInteger rangeIndex = [rangesToDelete count];
   1392     HFASSERT(rangeIndex > 0);
   1393     while (rangeIndex--) {
   1394         HFRange range = [rangesToDelete[rangeIndex] HFRange];
   1395         minSelection = llmin(range.location, minSelection);
   1396         if (range.length > 0) {
   1397             [byteArray deleteBytesInRange:range];
   1398             somethingWasDeleted = YES;
   1399         }
   1400     }
   1401     
   1402     HFASSERT(minSelection != ULLONG_MAX);
   1403     if (somethingWasDeleted) {
   1404         BEGIN_TRANSACTION();
   1405         [self _addPropertyChangeBits:HFControllerContentValue | HFControllerContentLength];
   1406         [self _setSingleSelectedContentsRange:HFRangeMake(minSelection, 0)];
   1407         [self _updateDisplayedRange];
   1408         END_TRANSACTION();
   1409     }
   1410     else {
   1411         NSBeep();
   1412     }
   1413 }
   1414 
   1415 - (void)_commandInsertByteArrays:(NSArray *)byteArrays inRanges:(NSArray *)ranges withSelectionAction:(HFControllerSelectAction)selectionAction {
   1416     HFASSERT(selectionAction < NUM_SELECTION_ACTIONS);
   1417     REQUIRE_NOT_NULL(byteArrays);
   1418     REQUIRE_NOT_NULL(ranges);
   1419     HFASSERT([ranges count] == [byteArrays count]);
   1420     NSUInteger index, max = [ranges count];
   1421     HFByteArray *bytes = [self byteArray];
   1422     HFASSERT(rangesAreInAscendingOrder([ranges objectEnumerator]));
   1423 
   1424     NSMutableArray *byteArraysToInsertOnUndo = [NSMutableArray arrayWithCapacity:max];
   1425     NSMutableArray *rangesToInsertOnUndo = [NSMutableArray arrayWithCapacity:max];
   1426     
   1427     BEGIN_TRANSACTION();
   1428     if (selectionAction == eSelectResult || selectionAction == eSelectAfterResult) {
   1429         [selectedContentsRanges removeAllObjects];
   1430     }
   1431     unsigned long long endOfInsertedRanges = ULLONG_MAX;
   1432     for (index = 0; index < max; index++) {
   1433         HFRange range = [ranges[index] HFRange];
   1434         HFByteArray *oldBytes = [bytes subarrayWithRange:range];
   1435         [byteArraysToInsertOnUndo addObject:oldBytes];
   1436         HFByteArray *newBytes = byteArrays[index];
   1437         EXPECT_CLASS(newBytes, [HFByteArray class]);
   1438         [bytes insertByteArray:newBytes inRange:range];
   1439         HFRange insertedRange = HFRangeMake(range.location, [newBytes length]);
   1440         HFRangeWrapper *insertedRangeWrapper = [HFRangeWrapper withRange:insertedRange];
   1441         [rangesToInsertOnUndo addObject:insertedRangeWrapper];
   1442         if (selectionAction == eSelectResult) {
   1443             [selectedContentsRanges addObject:insertedRangeWrapper];
   1444         }
   1445         else {
   1446             endOfInsertedRanges = HFMaxRange(insertedRange);
   1447         }
   1448     }
   1449     if (selectionAction == eSelectAfterResult) {
   1450         HFASSERT([ranges count] > 0);
   1451         [selectedContentsRanges addObject:[HFRangeWrapper withRange:HFRangeMake(endOfInsertedRanges, 0)]];
   1452     }
   1453     
   1454     if (selectionAction == ePreserveSelection) {
   1455         HFASSERT([selectedContentsRanges count] > 0);
   1456         [self _clipSelectedContentsRangesToLength:[self contentsLength]];
   1457     }
   1458     
   1459     VALIDATE_SELECTION();
   1460     HFASSERT([byteArraysToInsertOnUndo count] == [rangesToInsertOnUndo count]);
   1461     [self _registerUndoOperationForInsertingByteArrays:byteArraysToInsertOnUndo inRanges:rangesToInsertOnUndo withSelectionAction:(selectionAction == ePreserveSelection ? ePreserveSelection : eSelectAfterResult)];
   1462     [self _updateDisplayedRange];
   1463     [self maximizeVisibilityOfContentsRange:[selectedContentsRanges[0] HFRange]];
   1464     [self _addPropertyChangeBits:HFControllerContentValue | HFControllerContentLength | HFControllerSelectedRanges];
   1465     END_TRANSACTION();
   1466 }
   1467 
   1468 /* The user has hit undo after typing a string. */
   1469 - (void)_commandReplaceBytesAfterBytesFromBeginning:(unsigned long long)leftOffset upToBytesFromEnd:(unsigned long long)rightOffset withByteArray:(HFByteArray *)bytesToReinsert {
   1470     HFASSERT(bytesToReinsert != NULL);
   1471     
   1472     BEGIN_TRANSACTION();
   1473     HFByteArray *bytes = [self byteArray];
   1474     unsigned long long contentsLength = [self contentsLength];
   1475     HFASSERT(leftOffset <= contentsLength);
   1476     HFASSERT(rightOffset <= contentsLength);
   1477     HFASSERT(contentsLength - rightOffset >= leftOffset);
   1478     HFRange rangeToReplace = HFRangeMake(leftOffset, contentsLength - rightOffset - leftOffset);
   1479     [self _registerCondemnedRangesForUndo:[HFRangeWrapper withRanges:&rangeToReplace count:1] selectingRangesAfterUndo:NO];
   1480     [bytes insertByteArray:bytesToReinsert inRange:rangeToReplace];
   1481     [self _updateDisplayedRange];
   1482     [self _setSingleSelectedContentsRange:HFRangeMake(rangeToReplace.location, [bytesToReinsert length])];
   1483     [self _addPropertyChangeBits:HFControllerContentValue | HFControllerContentLength | HFControllerSelectedRanges];
   1484     END_TRANSACTION();
   1485 }
   1486 
   1487 /* We use NSNumbers instead of long longs here because Tiger/PPC NSInvocation had trouble with long longs */
   1488 - (void)_commandValueObjectsReplaceBytesAfterBytesFromBeginning:(NSNumber *)leftOffset upToBytesFromEnd:(NSNumber *)rightOffset withByteArray:(HFByteArray *)bytesToReinsert {
   1489     HFASSERT(leftOffset != NULL);
   1490     HFASSERT(rightOffset != NULL);
   1491     EXPECT_CLASS(leftOffset, NSNumber);
   1492     EXPECT_CLASS(rightOffset, NSNumber);
   1493     [self _commandReplaceBytesAfterBytesFromBeginning:[leftOffset unsignedLongLongValue] upToBytesFromEnd:[rightOffset unsignedLongLongValue] withByteArray:bytesToReinsert];
   1494 }
   1495 
   1496 - (void)moveInDirection:(HFControllerMovementDirection)direction byByteCount:(unsigned long long)amountToMove withSelectionTransformation:(HFControllerSelectionTransformation)transformation usingAnchor:(BOOL)useAnchor {
   1497     if (! useAnchor) selectionAnchor = NO_SELECTION;
   1498     switch (transformation) {
   1499         case HFControllerDiscardSelection:
   1500             [self _moveDirectionDiscardingSelection:direction byAmount:amountToMove];
   1501             break;
   1502             
   1503         case HFControllerShiftSelection:
   1504             [self _shiftSelectionInDirection:direction byAmount:amountToMove];
   1505             break;
   1506             
   1507         case HFControllerExtendSelection:
   1508             [self _extendSelectionInDirection:direction byAmount:amountToMove];
   1509             break;
   1510             
   1511         default:
   1512             [NSException raise:NSInvalidArgumentException format:@"Invalid transformation %ld", (long)transformation];
   1513             break;
   1514     }
   1515     if (! useAnchor) selectionAnchor = NO_SELECTION;
   1516 }
   1517 
   1518 - (void)moveInDirection:(HFControllerMovementDirection)direction withGranularity:(HFControllerMovementGranularity)granularity andModifySelection:(BOOL)extendSelection {
   1519     HFASSERT(granularity == HFControllerMovementByte || granularity == HFControllerMovementColumn || granularity == HFControllerMovementLine || granularity == HFControllerMovementPage || granularity == HFControllerMovementDocument);
   1520     HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
   1521     unsigned long long bytesToMove = 0;
   1522     switch (granularity) {
   1523         case HFControllerMovementByte:
   1524             bytesToMove = 1;
   1525             break;
   1526         case HFControllerMovementColumn:
   1527             /* This is a tricky case because the amount we have to move depends on our position in the column. */
   1528             bytesToMove = [self _distanceToWordBoundaryForDirection:direction];
   1529             break;
   1530         case HFControllerMovementLine:
   1531             bytesToMove = [self bytesPerLine];
   1532             break;
   1533         case HFControllerMovementPage:
   1534             bytesToMove = HFProductULL([self bytesPerLine], HFFPToUL(MIN(floorl([self displayedLineRange].length), 1.)));
   1535             break;
   1536         case HFControllerMovementDocument:
   1537             bytesToMove = [self contentsLength];
   1538             break;
   1539     }
   1540     HFControllerSelectionTransformation transformation = (extendSelection ? HFControllerExtendSelection : HFControllerDiscardSelection);
   1541     [self moveInDirection:direction byByteCount:bytesToMove withSelectionTransformation:transformation usingAnchor:YES];
   1542 }
   1543 
   1544 - (void)moveToLineBoundaryInDirection:(HFControllerMovementDirection)direction andModifySelection:(BOOL)modifySelection {
   1545     HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
   1546     BEGIN_TRANSACTION();
   1547     unsigned long long locationToMakeVisible;
   1548     HFRange additionalSelection;
   1549     
   1550     if (direction == HFControllerDirectionLeft) {
   1551         /* If we are at the beginning of a line, this should be a no-op */
   1552         unsigned long long minLocation = [self _minimumSelectionLocation];
   1553         unsigned long long newMinLocation = (minLocation / bytesPerLine) * bytesPerLine;
   1554         locationToMakeVisible = newMinLocation;
   1555         additionalSelection = HFRangeMake(newMinLocation, minLocation - newMinLocation);
   1556     }
   1557     else {
   1558         /* This always advances to the next line */
   1559         unsigned long long maxLocation = [self _maximumSelectionLocation];
   1560         unsigned long long proposedNewMaxLocation = HFRoundUpToNextMultipleSaturate(maxLocation, bytesPerLine);
   1561         unsigned long long newMaxLocation = MIN([self contentsLength], proposedNewMaxLocation);
   1562         HFASSERT(newMaxLocation >= maxLocation);
   1563         locationToMakeVisible = newMaxLocation;
   1564         additionalSelection = HFRangeMake(maxLocation, newMaxLocation - maxLocation);
   1565     }
   1566     
   1567     if (modifySelection) {
   1568         if (additionalSelection.length > 0) {
   1569             [self _addRangeToSelection:additionalSelection];
   1570             [self _addPropertyChangeBits:HFControllerSelectedRanges];
   1571         }
   1572     }
   1573     else {
   1574         [self _setSingleSelectedContentsRange:HFRangeMake(locationToMakeVisible, 0)];
   1575     }
   1576     [self _ensureVisibilityOfLocation:locationToMakeVisible];
   1577     END_TRANSACTION();
   1578 }
   1579 
   1580 - (void)deleteSelection {
   1581     if ([self editMode] == HFOverwriteMode || ! [self editable]) {
   1582         NSBeep();
   1583     }
   1584     else {
   1585         [self _commandDeleteRanges:[HFRangeWrapper organizeAndMergeRanges:selectedContentsRanges]];
   1586     }
   1587 }
   1588 
   1589 // Called after Replace All is finished. 
   1590 - (void)replaceByteArray:(HFByteArray *)newArray {
   1591     REQUIRE_NOT_NULL(newArray);
   1592     EXPECT_CLASS(newArray, HFByteArray);
   1593     HFRange entireRange = HFRangeMake(0, [self contentsLength]);
   1594     if ([self editMode] == HFOverwriteMode && [newArray length] != entireRange.length) {
   1595         NSBeep();
   1596     }
   1597     else {
   1598         [self _commandInsertByteArrays:@[newArray] inRanges:[HFRangeWrapper withRanges:&entireRange count:1] withSelectionAction:ePreserveSelection];
   1599     }
   1600 }
   1601 
   1602 - (BOOL)insertData:(NSData *)data replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing {
   1603     REQUIRE_NOT_NULL(data);
   1604     BOOL result;
   1605 #if ! NDEBUG
   1606     const unsigned long long startLength = [byteArray length];
   1607     unsigned long long expectedNewLength;
   1608     if ([self editMode] == HFOverwriteMode) {
   1609         expectedNewLength = startLength;
   1610     }    
   1611     else {
   1612         expectedNewLength = startLength + [data length] - previousBytes;
   1613         FOREACH(HFRangeWrapper*, wrapper, [self selectedContentsRanges]) expectedNewLength -= [wrapper HFRange].length;
   1614     }
   1615 #endif
   1616     HFByteSlice *slice = [[HFSharedMemoryByteSlice alloc] initWithUnsharedData:data];
   1617     HFASSERT([slice length] == [data length]);
   1618     HFByteArray *array = [[preferredByteArrayClass() alloc] init];
   1619     [array insertByteSlice:slice inRange:HFRangeMake(0, 0)];
   1620     HFASSERT([array length] == [data length]);
   1621     result = [self insertByteArray:array replacingPreviousBytes:previousBytes allowUndoCoalescing:allowUndoCoalescing];
   1622     [slice release];
   1623     [array release];
   1624 #if ! NDEBUG
   1625     HFASSERT((result && [byteArray length] == expectedNewLength) || (! result && [byteArray length] == startLength));
   1626 #endif
   1627     return result;
   1628 }
   1629 
   1630 - (BOOL)_insertionModeCoreInsertByteArray:(HFByteArray *)bytesToInsert replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing outNewSingleSelectedRange:(HFRange *)outSelectedRange {
   1631     HFASSERT([self editMode] == HFInsertMode);
   1632     REQUIRE_NOT_NULL(bytesToInsert);
   1633     
   1634     /* Guard against overflow.  If [bytesToInsert length] + [self contentsLength] - previousBytes overflows, then we can't do it */
   1635     HFASSERT([self contentsLength] >= previousBytes);
   1636     if (! HFSumDoesNotOverflow([bytesToInsert length], [self contentsLength] - previousBytes)) {
   1637         return NO; //don't do anything
   1638     }
   1639     
   1640     
   1641     unsigned long long amountDeleted = 0, amountAdded = [bytesToInsert length];
   1642     HFByteArray *bytes = [self byteArray];
   1643     
   1644     /* Delete all the selection - in reverse order - except the last (really first) one, which we will overwrite. */
   1645     NSArray *allRangesToRemove = [HFRangeWrapper organizeAndMergeRanges:[self selectedContentsRanges]];
   1646     HFRange rangeToReplace = [allRangesToRemove[0] HFRange];
   1647     HFASSERT(rangeToReplace.location == [self _minimumSelectionLocation]);
   1648     NSUInteger rangeIndex, rangeCount = [allRangesToRemove count];
   1649     HFASSERT(rangeCount > 0);
   1650     NSMutableArray *rangesToDelete = [NSMutableArray arrayWithCapacity:rangeCount - 1];
   1651     for (rangeIndex = rangeCount - 1; rangeIndex > 0; rangeIndex--) {
   1652         HFRangeWrapper *rangeWrapper = allRangesToRemove[rangeIndex];
   1653         HFRange range = [rangeWrapper HFRange];
   1654         if (range.length > 0) {
   1655             amountDeleted = HFSum(amountDeleted, range.length);
   1656             [rangesToDelete insertObject:rangeWrapper atIndex:0];
   1657         }
   1658     }
   1659     
   1660     if ([rangesToDelete count] > 0) {
   1661         HFASSERT(rangesAreInAscendingOrder([rangesToDelete objectEnumerator]));
   1662         /* TODO: This is problematic because it overwrites the selection that gets set by _activateTypingUndoCoalescingForReplacingRange:, so we lose the first selection in a multiple selection scenario. */
   1663         [self _registerCondemnedRangesForUndo:rangesToDelete selectingRangesAfterUndo:YES];
   1664         NSEnumerator *enumer = [rangesToDelete reverseObjectEnumerator];
   1665         HFRangeWrapper *rangeWrapper;
   1666         while ((rangeWrapper = [enumer nextObject])) {
   1667             [bytes deleteBytesInRange:[rangeWrapper HFRange]];
   1668         }
   1669     }
   1670     
   1671     rangeToReplace.length = HFSum(rangeToReplace.length, previousBytes);
   1672     
   1673     /* Insert data */
   1674 #if ! NDEBUG
   1675     unsigned long long expectedLength = [byteArray length] + [bytesToInsert length] - rangeToReplace.length;
   1676 #endif
   1677     [byteArray insertByteArray:bytesToInsert inRange:rangeToReplace];
   1678 #if ! NDEBUG
   1679     HFASSERT(expectedLength == [byteArray length]);
   1680 #endif
   1681     
   1682     /* return the new selected range */
   1683     *outSelectedRange = HFRangeMake(HFSum(rangeToReplace.location, amountAdded), 0);
   1684     return YES;
   1685 }
   1686 
   1687 
   1688 - (BOOL)_overwriteModeCoreInsertByteArray:(HFByteArray *)bytesToInsert replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing outRangeToRemoveFromSelection:(HFRange *)outRangeToRemove {
   1689     REQUIRE_NOT_NULL(bytesToInsert);
   1690     const unsigned long long byteArrayLength = [byteArray length];
   1691     const unsigned long long bytesToInsertLength = [bytesToInsert length];
   1692     HFRange firstSelectedRange = [selectedContentsRanges[0] HFRange];
   1693     HFRange proposedRangeToOverwrite = HFRangeMake(firstSelectedRange.location, bytesToInsertLength);
   1694     HFASSERT(proposedRangeToOverwrite.location >= previousBytes);
   1695     proposedRangeToOverwrite.location -= previousBytes;
   1696     if (! HFRangeIsSubrangeOfRange(proposedRangeToOverwrite, HFRangeMake(0, byteArrayLength))) {
   1697         /* The user tried to overwrite past the end */
   1698         NSBeep();
   1699         return NO;
   1700     }
   1701     
   1702     [byteArray insertByteArray:bytesToInsert inRange:proposedRangeToOverwrite];
   1703     
   1704     *outRangeToRemove = proposedRangeToOverwrite;
   1705     return YES;
   1706 }
   1707 
   1708 - (BOOL)insertByteArray:(HFByteArray *)bytesToInsert replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing {
   1709 #if ! NDEBUG
   1710     if (previousBytes > 0) {
   1711         NSArray *selectedRanges = [self selectedContentsRanges];
   1712         HFASSERT([selectedRanges count] == 1);
   1713         HFRange selectedRange = [selectedRanges[0] HFRange];
   1714         HFASSERT(selectedRange.location >= previousBytes); //don't try to delete more trailing bytes than we actually have!
   1715     }
   1716 #endif
   1717     REQUIRE_NOT_NULL(bytesToInsert);
   1718     
   1719     
   1720     BEGIN_TRANSACTION();
   1721     unsigned long long beforeLength = [byteArray length];
   1722     BOOL inOverwriteMode = [self editMode] == HFOverwriteMode;
   1723     HFRange modificationRange; //either range to remove from selection if in overwrite mode, or range to select if not
   1724     BOOL success;
   1725     if (inOverwriteMode) {
   1726         success = [self _overwriteModeCoreInsertByteArray:bytesToInsert replacingPreviousBytes:previousBytes allowUndoCoalescing:allowUndoCoalescing outRangeToRemoveFromSelection:&modificationRange];
   1727     }
   1728     else {
   1729         success = [self _insertionModeCoreInsertByteArray:bytesToInsert replacingPreviousBytes:previousBytes allowUndoCoalescing:allowUndoCoalescing outNewSingleSelectedRange:&modificationRange];
   1730     }
   1731     
   1732     if (success) {
   1733         /* Update our selection */
   1734         [self _addPropertyChangeBits:HFControllerContentValue];
   1735         [self _updateDisplayedRange];
   1736         [self _addPropertyChangeBits:HFControllerContentValue];
   1737         if (inOverwriteMode) {
   1738             [self _removeRangeFromSelection:modificationRange withCursorLocationIfAllSelectionRemoved:HFMaxRange(modificationRange)];
   1739             [self maximizeVisibilityOfContentsRange:[selectedContentsRanges[0] HFRange]];
   1740         }
   1741         else {
   1742             [self _setSingleSelectedContentsRange:modificationRange];
   1743             [self maximizeVisibilityOfContentsRange:modificationRange];
   1744         }
   1745         if (beforeLength != [byteArray length]) [self _addPropertyChangeBits:HFControllerContentLength];
   1746     }
   1747     END_TRANSACTION();
   1748     return success;
   1749 }
   1750 
   1751 - (void)deleteDirection:(HFControllerMovementDirection)direction {
   1752     HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
   1753     if ([self editMode] != HFInsertMode || ! [self editable]) {
   1754         NSBeep();
   1755         return;
   1756     }
   1757     unsigned long long minSelection = [self _minimumSelectionLocation];
   1758     unsigned long long maxSelection = [self _maximumSelectionLocation];
   1759     if (maxSelection != minSelection) {
   1760         [self deleteSelection];
   1761     }
   1762     else {
   1763         HFRange rangeToDelete = HFRangeMake(minSelection, 1);
   1764         BOOL rangeIsValid;
   1765         if (direction == HFControllerDirectionLeft) {
   1766             rangeIsValid = (rangeToDelete.location > 0);
   1767             rangeToDelete.location--;
   1768         }
   1769         else {
   1770             rangeIsValid = (rangeToDelete.location < [self contentsLength]);
   1771         }
   1772         if (rangeIsValid) {
   1773             BEGIN_TRANSACTION();
   1774             [byteArray deleteBytesInRange:rangeToDelete];
   1775             [self _setSingleSelectedContentsRange:HFRangeMake(rangeToDelete.location, 0)];
   1776             [self _updateDisplayedRange];
   1777             [self _addPropertyChangeBits:HFControllerSelectedRanges | HFControllerContentValue | HFControllerContentLength];
   1778             END_TRANSACTION();
   1779         }
   1780     }
   1781 }
   1782 
   1783 - (HFEditMode)editMode {
   1784     return _hfflags.editMode;
   1785 }
   1786 
   1787 - (void)setEditMode:(HFEditMode)val
   1788 {
   1789     if (val != _hfflags.editMode) {
   1790         _hfflags.editMode = val;
   1791         // don't allow undo coalescing when switching modes
   1792         [self _addPropertyChangeBits:HFControllerEditable];        
   1793     }
   1794 }
   1795 
   1796 - (void)reloadData {
   1797     BEGIN_TRANSACTION();
   1798     [cachedData release];
   1799     cachedData = nil;
   1800     [self _updateDisplayedRange];
   1801     [self _addPropertyChangeBits: HFControllerContentValue | HFControllerContentLength];
   1802     END_TRANSACTION();
   1803 }
   1804 
   1805 #if BENCHMARK_BYTEARRAYS
   1806 
   1807 + (void)_testByteArray {
   1808     HFByteArray* first = [[[HFFullMemoryByteArray alloc] init] autorelease];
   1809     HFBTreeByteArray* second = [[[HFBTreeByteArray alloc] init] autorelease];    
   1810     first = nil;
   1811     //    second = nil;
   1812     
   1813     //srandom(time(NULL));
   1814     
   1815     unsigned opCount = 4096 * 512;
   1816     unsigned long long expectedLength = 0;
   1817     unsigned i;
   1818     for (i=1; i <= opCount; i++) {
   1819         @autoreleasepool {
   1820         NSUInteger op;
   1821         const unsigned long long length = [first length];
   1822         unsigned long long offset;
   1823         unsigned long long number;
   1824         switch ((op = (random()%2))) {
   1825             case 0: { //insert
   1826                 offset = random() % (1 + length);
   1827                 HFByteSlice* slice = [[HFRandomDataByteSlice alloc] initWithRandomDataLength: 1 + random() % 1000];
   1828                 [first insertByteSlice:slice inRange:HFRangeMake(offset, 0)];
   1829                 [second insertByteSlice:slice inRange:HFRangeMake(offset, 0)];
   1830                 expectedLength += [slice length];
   1831                 [slice release];
   1832                 break;
   1833             }
   1834             case 1: { //delete
   1835                 if (length > 0) {
   1836                     offset = random() % length;
   1837                     number = 1 + random() % (length - offset);
   1838                     [first deleteBytesInRange:HFRangeMake(offset, number)];
   1839                     [second deleteBytesInRange:HFRangeMake(offset, number)];
   1840                     expectedLength -= number;
   1841                 }
   1842                 break;
   1843             }
   1844         }
   1845         } // @autoreleasepool
   1846     }
   1847 }
   1848 
   1849 + (void)_testAttributeArrays {
   1850     HFByteRangeAttributeArray *naiveTree = [[HFNaiveByteRangeAttributeArray alloc] init];
   1851     HFAnnotatedTreeByteRangeAttributeArray *smartTree = [[HFAnnotatedTreeByteRangeAttributeArray alloc] init];
   1852     naiveTree = nil;
   1853     //    smartTree = nil;
   1854     
   1855     NSString * const attributes[3] = {@"Alpha", @"Beta", @"Gamma"};
   1856     
   1857     const NSUInteger supportedIndexEnd = NSNotFound;
   1858     NSUInteger round;
   1859     for (round = 0; round < 4096 * 256; round++) {
   1860         NSString *attribute = attributes[random() % (sizeof attributes / sizeof *attributes)];
   1861         BOOL insert = ([smartTree isEmpty] || [naiveTree isEmpty] || (random() % 2));
   1862         
   1863         unsigned long long end = random();
   1864         unsigned long long start = random();
   1865         if (end < start) {
   1866             unsigned long long temp = end;
   1867             end = start;
   1868             start = temp;
   1869         }
   1870         HFRange range = HFRangeMake(start, end - start);
   1871         
   1872         if (insert) {
   1873             [naiveTree addAttribute:attribute range:range];
   1874             [smartTree addAttribute:attribute range:range];
   1875         }
   1876         else {
   1877             [naiveTree removeAttribute:attribute range:range];
   1878             [smartTree removeAttribute:attribute range:range];
   1879         }
   1880     }
   1881     
   1882     [naiveTree release];
   1883     [smartTree release];
   1884 }
   1885 
   1886 
   1887 + (void)initialize {
   1888     CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
   1889     srandom(0);
   1890     [self _testByteArray];
   1891     CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
   1892     printf("Byte array time: %f\n", end - start);
   1893     
   1894     srandom(0);
   1895     start = CFAbsoluteTimeGetCurrent();
   1896     [self _testAttributeArrays];
   1897     end = CFAbsoluteTimeGetCurrent();
   1898     printf("Attribute array time: %f\n", end - start);
   1899     
   1900     exit(0);
   1901 }
   1902 
   1903 #endif
   1904 
   1905 @end

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.