File: HFHexTextRepresenter.m

package info (click to toggle)
sameboy 1.0.2%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 10,632 kB
  • sloc: ansic: 29,954; objc: 22,249; asm: 1,424; pascal: 1,373; makefile: 1,064; xml: 111
file content (203 lines) | stat: -rw-r--r-- 7,614 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//
//  HFHexTextRepresenter.m
//  HexFiend_2
//
//  Copyright 2007 ridiculous_fish. All rights reserved.
//

#import <HexFiend/HFHexTextRepresenter.h>
#import <HexFiend/HFRepresenterHexTextView.h>
#import <HexFiend/HFPasteboardOwner.h>

@interface HFHexPasteboardOwner : HFPasteboardOwner {
    NSUInteger _bytesPerColumn;
}
@property (nonatomic) NSUInteger bytesPerColumn;
@end

static inline unsigned char hex2char(NSUInteger c) {
    HFASSERT(c < 16);
    return "0123456789ABCDEF"[c];
}

@implementation HFHexPasteboardOwner

@synthesize bytesPerColumn = _bytesPerColumn;

- (unsigned long long)stringLengthForDataLength:(unsigned long long)dataLength {
    if(!dataLength) return 0;
    // -1 because no trailing space for an exact multiple.
    unsigned long long spaces = _bytesPerColumn ? (dataLength-1)/_bytesPerColumn : 0;
    if ((ULLONG_MAX - spaces)/2 <= dataLength) return ULLONG_MAX;
    else return dataLength*2 + spaces;
}

- (void)writeDataInBackgroundToPasteboard:(NSPasteboard *)pboard ofLength:(unsigned long long)length forType:(NSString *)type trackingProgress:(id)tracker {
    HFASSERT([type isEqual:NSStringPboardType]);
    if(length == 0) {
        [pboard setString:@"" forType:type];
        return;
    }
    HFByteArray *byteArray = [self byteArray];
    HFASSERT(length <= NSUIntegerMax);
    NSUInteger dataLength = ll2l(length);
    NSUInteger stringLength = ll2l([self stringLengthForDataLength:length]);
    HFASSERT(stringLength < ULLONG_MAX);
    NSUInteger offset = 0, stringOffset = 0, remaining = dataLength;
    unsigned char * restrict const stringBuffer = check_malloc(stringLength);
    while (remaining > 0) {
        unsigned char dataBuffer[64 * 1024];
        NSUInteger amountToCopy = MIN(sizeof dataBuffer, remaining);
        NSUInteger bound = offset + amountToCopy - 1;
        [byteArray copyBytes:dataBuffer range:HFRangeMake(offset, amountToCopy)];
        
        if(_bytesPerColumn > 0 && offset > 0) { // ensure offset > 0 to skip adding a leading space
            NSUInteger left = _bytesPerColumn - (offset % _bytesPerColumn);
            if(left != _bytesPerColumn) {
                while(left-- > 0 && offset <= bound) {
                    unsigned char c = dataBuffer[offset++];
                    stringBuffer[stringOffset] = hex2char(c >> 4);
                    stringBuffer[stringOffset + 1] = hex2char(c & 0xF);
                    stringOffset += 2;
                }
            }
            if(offset <= bound)
                stringBuffer[stringOffset++] = ' ';
        }
        
        if(_bytesPerColumn > 0) while(offset+_bytesPerColumn <= bound) {
            for(NSUInteger j = 0; j < _bytesPerColumn; j++) {
                unsigned char c = dataBuffer[offset++];
                stringBuffer[stringOffset] = hex2char(c >> 4);
                stringBuffer[stringOffset + 1] = hex2char(c & 0xF);
                stringOffset += 2;
            }
            stringBuffer[stringOffset++] = ' ';
        }
        
        while (offset <= bound) {
            unsigned char c = dataBuffer[offset++];
            stringBuffer[stringOffset] = hex2char(c >> 4);
            stringBuffer[stringOffset + 1] = hex2char(c & 0xF);
            stringOffset += 2;
        }
        
        remaining -= amountToCopy;
    }

    NSString *string = [[NSString alloc] initWithBytesNoCopy:stringBuffer length:stringLength encoding:NSASCIIStringEncoding freeWhenDone:YES];
    [pboard setString:string forType:type];
    [string release];
}

@end

@implementation HFHexTextRepresenter

/* No extra NSCoder support needed */

- (Class)_textViewClass {
    return [HFRepresenterHexTextView class];
}

- (void)initializeView {
    [super initializeView];
    [[self view] setBytesBetweenVerticalGuides:4];
    unpartneredLastNybble = UCHAR_MAX;
    omittedNybbleLocation = ULLONG_MAX;
}

+ (NSPoint)defaultLayoutPosition {
    return NSMakePoint(0, 0);
}

- (void)_clearOmittedNybble {
    unpartneredLastNybble = UCHAR_MAX;
    omittedNybbleLocation = ULLONG_MAX;
}

- (BOOL)_insertionShouldDeleteLastNybble {
    /* Either both the omittedNybbleLocation and unpartneredLastNybble are invalid (set to their respective maxima), or neither are */
    HFASSERT((omittedNybbleLocation == ULLONG_MAX) == (unpartneredLastNybble == UCHAR_MAX));
    /* We should delete the last nybble if our omittedNybbleLocation is the point where we would insert */
    BOOL result = NO;
    if (omittedNybbleLocation != ULLONG_MAX) {
        HFController *controller = [self controller];
        NSArray *selectedRanges = [controller selectedContentsRanges];
        if ([selectedRanges count] == 1) {
            HFRange selectedRange = [selectedRanges[0] HFRange];
            result = (selectedRange.length == 0 && selectedRange.location > 0 && selectedRange.location - 1 == omittedNybbleLocation);
        }
    }
    return result;
}

- (BOOL)_canInsertText:(NSString *)text {
    REQUIRE_NOT_NULL(text);
    NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
    return [text rangeOfCharacterFromSet:characterSet].location != NSNotFound;
}

- (void)insertText:(NSString *)text {
    REQUIRE_NOT_NULL(text);
    if (! [self _canInsertText:text]) {
        /* The user typed invalid data, and we can ignore it */
        return;
    }
    
    BOOL shouldReplacePriorByte = [self _insertionShouldDeleteLastNybble];
    if (shouldReplacePriorByte) {
        HFASSERT(unpartneredLastNybble < 16);
        /* Prepend unpartneredLastNybble as a nybble */
        text = [NSString stringWithFormat:@"%1X%@", unpartneredLastNybble, text];
    }
    BOOL isMissingLastNybble;
    NSData *data = HFDataFromHexString(text, &isMissingLastNybble);
    HFASSERT([data length] > 0);
    HFASSERT(shouldReplacePriorByte != isMissingLastNybble);
    HFController *controller = [self controller];
    BOOL success = [controller insertData:data replacingPreviousBytes: (shouldReplacePriorByte ? 1 : 0) allowUndoCoalescing:YES];
    if (isMissingLastNybble && success) {
        HFASSERT([data length] > 0);
        HFASSERT(unpartneredLastNybble == UCHAR_MAX);
        [data getBytes:&unpartneredLastNybble range:NSMakeRange([data length] - 1, 1)];
        NSArray *selectedRanges = [controller selectedContentsRanges];
        HFASSERT([selectedRanges count] >= 1);
        HFRange selectedRange = [selectedRanges[0] HFRange];
        HFASSERT(selectedRange.location > 0);
        omittedNybbleLocation = HFSubtract(selectedRange.location, 1);
    }
    else {
        [self _clearOmittedNybble];
    }
}

- (NSData *)dataFromPasteboardString:(NSString *)string {
    REQUIRE_NOT_NULL(string);
    return HFDataFromHexString(string, NULL);
}

- (void)controllerDidChange:(HFControllerPropertyBits)bits {
    if (bits & HFControllerHideNullBytes) {
        [[self view] setHidesNullBytes:[[self controller] shouldHideNullBytes]];
    }
    [super controllerDidChange:bits];
    if (bits & (HFControllerSelectedRanges)) {
        [self _clearOmittedNybble];
    }
}

- (void)copySelectedBytesToPasteboard:(NSPasteboard *)pb {
    REQUIRE_NOT_NULL(pb);
    HFByteArray *selection = [[self controller] byteArrayForSelectedContentsRanges];
    HFASSERT(selection != NULL);
    if ([selection length] == 0) {
        NSBeep();
    } else {
        HFHexPasteboardOwner *owner = [HFHexPasteboardOwner ownPasteboard:pb forByteArray:selection withTypes:@[HFPrivateByteArrayPboardType, NSStringPboardType]];
        [owner setBytesPerLine:[self bytesPerLine]];
        owner.bytesPerColumn = self.bytesPerColumn;
    }
}

@end