Optimisation for reading large properties: avoid memory reallocation and copying data.

This commit is contained in:
rfm 2024-03-01 19:29:01 +00:00
parent f6707b897a
commit e66906ef38

View file

@ -958,12 +958,14 @@ xErrorHandler(Display *d, XErrorEvent *e)
[self setOwnedByOpenStep: NO]; [self setOwnedByOpenStep: NO];
} }
- (NSMutableData*) getSelectionData: (XSelectionEvent*)xEvent - (long) getSelectionData: (XSelectionEvent*)xEvent
type: (Atom*)type type: (Atom*)type
size: (long)max size: (long)max
into: (NSMutableData*)md
{ {
int status; int status;
unsigned char *data; unsigned char *data;
long bytes_added = 0L;
long long_offset = 0L; long long_offset = 0L;
long long_length = FULL_LENGTH; long long_length = FULL_LENGTH;
Atom req_type = AnyPropertyType; Atom req_type = AnyPropertyType;
@ -971,7 +973,7 @@ xErrorHandler(Display *d, XErrorEvent *e)
int actual_format; int actual_format;
unsigned long bytes_remaining; unsigned long bytes_remaining;
unsigned long number_items; unsigned long number_items;
NSMutableData *md = nil; BOOL initial = YES;
if (max > long_length) long_length = max; if (max > long_length) long_length = max;
/* /*
@ -1016,12 +1018,20 @@ xErrorHandler(Display *d, XErrorEvent *e)
count = number_items * actual_format / 8; count = number_items * actual_format / 8;
} }
if (md == nil) if (initial)
{ {
NSUInteger capacity = [md capacity];
int space = capacity - [md length];
int need = count + bytes_remaining;
/* data buffer needs to be big enough for the whole property /* data buffer needs to be big enough for the whole property
*/ */
md = [[NSMutableData alloc] initial = NO;
initWithCapacity: count + bytes_remaining]; if (space < need)
{
capacity += need - space;
[md setCapacity: capacity];
}
req_type = actual_type; req_type = actual_type;
} }
else if (req_type != actual_type) else if (req_type != actual_type)
@ -1033,15 +1043,14 @@ xErrorHandler(Display *d, XErrorEvent *e)
req_name, act_name); req_name, act_name);
XFree(req_name); XFree(req_name);
XFree(act_name); XFree(act_name);
RELEASE(md);
if (data) if (data)
{ {
XFree(data); XFree(data);
} }
return nil; return 0;
} }
[md appendBytes: (void *)data length: count]; [md appendBytes: (void *)data length: count];
bytes_added += count;
long_offset += count / 4; long_offset += count / 4;
if (data) if (data)
{ {
@ -1054,19 +1063,18 @@ xErrorHandler(Display *d, XErrorEvent *e)
if (status == Success) if (status == Success)
{ {
*type = actual_type; *type = actual_type;
return AUTORELEASE(md); return bytes_added;
} }
else else
{ {
RELEASE(md); return 0;
return nil;
} }
} }
- (void) xSelectionNotify: (XSelectionEvent*)xEvent - (void) xSelectionNotify: (XSelectionEvent*)xEvent
{ {
Atom actual_type; Atom actual_type;
NSMutableData *md = nil; NSMutableData *md;
if (xEvent->property == (Atom)None) if (xEvent->property == (Atom)None)
{ {
@ -1083,9 +1091,8 @@ xErrorHandler(Display *d, XErrorEvent *e)
} }
[self setWaitingForSelection: 0]; [self setWaitingForSelection: 0];
md = [self getSelectionData: xEvent type: &actual_type size: 0]; md = [NSMutableData dataWithCapacity: FULL_LENGTH];
if ([self getSelectionData: xEvent type: &actual_type size: 0 into: md] > 0)
if (md != nil)
{ {
unsigned count = 0; unsigned count = 0;
@ -1102,6 +1109,15 @@ xErrorHandler(Display *d, XErrorEvent *e)
NSDebugMLLog(@"INCR", NSDebugMLLog(@"INCR",
@"Size for INCR chunks is %u bytes.", (unsigned)size); @"Size for INCR chunks is %u bytes.", (unsigned)size);
[md setLength: 0]; [md setLength: 0];
/* We expect to read multiple chunks, so to avoid excessive
* reallocation of memory we grow the buffer to be big enough
* to hold ten of them.
*/
if (size * 10 > [md capacity])
{
[md setCapacity: size * 10];
}
while (wait) while (wait)
{ {
XNextEvent(xDisplay, &event); XNextEvent(xDisplay, &event);
@ -1109,34 +1125,30 @@ xErrorHandler(Display *d, XErrorEvent *e)
if (event.type == PropertyNotify if (event.type == PropertyNotify
&& event.xproperty.state == PropertyNewValue) && event.xproperty.state == PropertyNewValue)
{ {
ENTER_POOL long length;
NSMutableData *imd;
imd = [self getSelectionData: xEvent
type: &actual_type
size: size];
/* Getting the property data also deletes the property, /* Getting the property data also deletes the property,
* telling the other end to send the next chunk. * telling the other end to send the next chunk.
* An empty chunk indicates end of transfer. * An empty chunk indicates end of transfer.
*/ */
if ([imd length] > 0) if ((length = [self getSelectionData: xEvent
type: &actual_type
size: size
into: md]) > 0)
{ {
if (GSDebugSet(@"INCR")) if (GSDebugSet(@"INCR"))
{ {
char *name = XGetAtomName(xDisplay, actual_type); char *name = XGetAtomName(xDisplay, actual_type);
NSLog(@"Retrieved %lu bytes type '%s'" NSLog(@"Retrieved %ld bytes type '%s'"
@" from X selection.", @" from X selection.", length, name);
(unsigned long)[imd length], name);
XFree(name); XFree(name);
} }
count++; count++;
[md appendData: imd];
} }
else else
{ {
wait = NO; wait = NO;
} }
LEAVE_POOL
} }
} }
if ([md length] == 0) if ([md length] == 0)
@ -1164,7 +1176,7 @@ xErrorHandler(Display *d, XErrorEvent *e)
} }
} }
if (md != nil) if ([md length] > 0)
{ {
// Convert data to text string. // Convert data to text string.
if (actual_type == XG_UTF8_STRING) if (actual_type == XG_UTF8_STRING)