Rewritten NSDirectoryEnumerator for speed

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@9633 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
nico 2001-04-19 22:09:25 +00:00
parent dc4fd8a18a
commit c5ac2a74eb
3 changed files with 307 additions and 204 deletions

View file

@ -1,3 +1,15 @@
2001-04-20 Nicola Pero <n.pero@mi.flashnet.it>
* Source/NSFileManager.m (NSDirectoryEnumerator): Rewritten to be
faster. ([NSFileManager -directoryContentsAtPath:]),
([NSFileManager -subpathsAtPath:]): Use the new init method for
NSDirectoryEnumerator. ([NSFileManager
-contentsEqualAtPath:andPath:]): Spare some method calls.
([NSFileManager -fileSystemRepresentationWithPath:]): Cleaned
preprocessor directives.
* Headers/gnustep/base/NSFileManager.h (NSDirectoryEnumerator):
Changed ivars in sync.
2001-04-19 Nicola Pero <nicola@brainstorm.co.uk> 2001-04-19 Nicola Pero <nicola@brainstorm.co.uk>
* Documentation/gsdoc/NSFunctions.gsdoc: Documented * Documentation/gsdoc/NSFunctions.gsdoc: Documented

View file

@ -1,4 +1,4 @@
/* /* -*-objc-*-
NSFileManager.h NSFileManager.h
Copyright (C) 1997,1999 Free Software Foundation, Inc. Copyright (C) 1997,1999 Free Software Foundation, Inc.
@ -120,24 +120,23 @@
@interface NSDirectoryEnumerator : NSEnumerator @interface NSDirectoryEnumerator : NSEnumerator
{ {
NSMutableArray *_enumStack; void *_stack; /* GSIArray */
NSMutableArray *_pathStack; char *_top_path;
NSString *_currentFileName; char *_current_file_path;
NSString *_currentFilePath; NSString *(*_stringWithFileSysImp)(id, SEL, char *, unsigned);
NSString *_topPath; struct
NSDictionary *_directoryAttributes; {
NSDictionary *_fileAttributes; BOOL isRecursive: 1;
struct { BOOL isFollowing: 1;
BOOL isRecursive: 1; BOOL justContents: 1;
BOOL isFollowing: 1; } _flags;
} _flags;
} }
// Initializing // Initializing
- (id) initWithDirectoryPath: (NSString*)path - (id) initWithDirectoryPath: (NSString*)path
recurseIntoSubdirectories: (BOOL)recurse recurseIntoSubdirectories: (BOOL)recurse
followSymlinks: (BOOL)follow followSymlinks: (BOOL)follow
prefixFiles: (BOOL)prefix; justContents: (BOOL)justContents;
// Getting attributes // Getting attributes
- (NSDictionary*) directoryAttributes; - (NSDictionary*) directoryAttributes;

View file

@ -8,6 +8,10 @@
Date: Feb 1997 Date: Feb 1997
Updates and fixes: Richard Frith-Macdonald Updates and fixes: Richard Frith-Macdonald
Author: Nicola Pero <n.pero@mi.flashnet.it>
Date: Apr 2001
Rewritten NSDirectoryEnumerator
This file is part of the GNUstep Base Library. This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@ -54,18 +58,6 @@
# include <windows.h> # include <windows.h>
#endif #endif
#if !defined(_POSIX_VERSION)
# if defined(NeXT)
# define DIR_enum_item struct direct
# endif
#endif
#if !defined(DIR_enum_item)
# define DIR_enum_item struct dirent
#endif
#define DIR_enum_state DIR
#if defined(__MINGW__) #if defined(__MINGW__)
#define WIN32ERR ((DWORD)0xFFFFFFFF) #define WIN32ERR ((DWORD)0xFFFFFFFF)
#endif #endif
@ -748,13 +740,13 @@ static NSFileManager* defaultManager = nil;
{ {
NSArray *a1 = [self directoryContentsAtPath: path1]; NSArray *a1 = [self directoryContentsAtPath: path1];
NSArray *a2 = [self directoryContentsAtPath: path2]; NSArray *a2 = [self directoryContentsAtPath: path2];
unsigned index; unsigned index, count = [a1 count];
BOOL ok = YES; BOOL ok = YES;
if ([a1 isEqual: a2] == NO) if ([a1 isEqual: a2] == NO)
return NO; return NO;
for (index = 0; ok == YES && index < [a1 count]; index++) for (index = 0; ok == YES && index < count; index++)
{ {
NSString *n = [a1 objectAtIndex: index]; NSString *n = [a1 objectAtIndex: index];
NSString *p1; NSString *p1;
@ -1159,11 +1151,15 @@ static NSFileManager* defaultManager = nil;
*/ */
if ([self fileExistsAtPath: path isDirectory: &is_dir] == NO || is_dir == NO) if ([self fileExistsAtPath: path isDirectory: &is_dir] == NO || is_dir == NO)
return nil; return nil;
/* We initialize the directory enumerator with justContents == YES,
which tells the NSDirectoryEnumerator code that we only enumerate
the contents non-recursively once, and exit. NSDirectoryEnumerator
can perform some optms using this assumption. */
direnum = [[NSDirectoryEnumerator alloc] initWithDirectoryPath: path direnum = [[NSDirectoryEnumerator alloc] initWithDirectoryPath: path
recurseIntoSubdirectories: NO recurseIntoSubdirectories: NO
followSymlinks: NO followSymlinks: NO
prefixFiles: NO]; justContents: YES];
content = [NSMutableArray arrayWithCapacity: 128]; content = [NSMutableArray arrayWithCapacity: 128];
nxtImp = [direnum methodForSelector: @selector(nextObject)]; nxtImp = [direnum methodForSelector: @selector(nextObject)];
@ -1180,10 +1176,10 @@ static NSFileManager* defaultManager = nil;
- (NSDirectoryEnumerator*) enumeratorAtPath: (NSString*)path - (NSDirectoryEnumerator*) enumeratorAtPath: (NSString*)path
{ {
return AUTORELEASE([[NSDirectoryEnumerator alloc] return AUTORELEASE([[NSDirectoryEnumerator alloc]
initWithDirectoryPath: path initWithDirectoryPath: path
recurseIntoSubdirectories: YES recurseIntoSubdirectories: YES
followSymlinks: NO followSymlinks: NO
prefixFiles: YES]); justContents: NO]);
} }
- (NSArray*) subpathsAtPath: (NSString*)path - (NSArray*) subpathsAtPath: (NSString*)path
@ -1193,23 +1189,24 @@ static NSFileManager* defaultManager = nil;
BOOL isDir; BOOL isDir;
IMP nxtImp; IMP nxtImp;
IMP addImp; IMP addImp;
if (![self fileExistsAtPath: path isDirectory: &isDir] || !isDir) if (![self fileExistsAtPath: path isDirectory: &isDir] || !isDir)
return nil; return nil;
direnum = [[NSDirectoryEnumerator alloc] direnum = [[NSDirectoryEnumerator alloc] initWithDirectoryPath: path
initWithDirectoryPath: path recurseIntoSubdirectories: YES
recurseIntoSubdirectories: YES followSymlinks: NO
followSymlinks: NO justContents: NO];
prefixFiles: YES];
content = [NSMutableArray arrayWithCapacity: 128]; content = [NSMutableArray arrayWithCapacity: 128];
nxtImp = [direnum methodForSelector: @selector(nextObject)]; nxtImp = [direnum methodForSelector: @selector(nextObject)];
addImp = [content methodForSelector: @selector(addObject:)]; addImp = [content methodForSelector: @selector(addObject:)];
while ((path = (*nxtImp)(direnum, @selector(nextObject))) != nil) while ((path = (*nxtImp)(direnum, @selector(nextObject))) != nil)
(*addImp)(content, @selector(addObject:), path); {
(*addImp)(content, @selector(addObject:), path);
}
RELEASE(direnum); RELEASE(direnum);
return content; return content;
@ -1252,7 +1249,7 @@ static NSFileManager* defaultManager = nil;
{ {
#ifdef __MINGW__ #ifdef __MINGW__
/* If path is in Unix format, transmorgrify it so Windows functions /* If path is in Unix format, transmorgrify it so Windows functions
can handle it */ can handle it */
NSString *newpath = path; NSString *newpath = path;
const char *c_path = [path cString]; const char *c_path = [path cString];
if (c_path[0] == '/' && c_path[1] == '/' && isalpha(c_path[2])) if (c_path[0] == '/' && c_path[1] == '/' && isalpha(c_path[2]))
@ -1283,8 +1280,9 @@ static NSFileManager* defaultManager = nil;
} }
/* FIXME: Should we translate relative paths? */ /* FIXME: Should we translate relative paths? */
return [newpath cString]; return [newpath cString];
#endif #else
return [path cString]; return [path cString];
#endif
} }
- (NSString*) stringWithFileSystemRepresentation: (const char*)string - (NSString*) stringWithFileSystemRepresentation: (const char*)string
@ -1297,127 +1295,82 @@ static NSFileManager* defaultManager = nil;
/* /*
* NSDirectoryEnumerator implementation * NSDirectoryEnumerator implementation
*
* The Objective-C interface hides a traditional C implementation.
* This was the only way I could get near the speed of standard unix
* tools for big directories.
*/ */
typedef struct _GSEnumeratedDirectory {
char *path;
DIR *pointer;
} GSEnumeratedDirectory;
inline void gsedRelease(GSEnumeratedDirectory X)
{
free(X.path);
closedir(X.pointer);
}
#define GSI_ARRAY_TYPES 0
#define GSI_ARRAY_EXTRA GSEnumeratedDirectory
#define GSI_ARRAY_RELEASE(X) gsedRelease(X.ext)
#define GSI_ARRAY_RETAIN(X)
#include <base/GSIArray.h>
/* The return value of this function is to be freed by using NSZoneFree().
The function takes for granted that path and file are correct
filesystem paths; that path does not end with a path separator, and
file does not begin with a path separator. */
inline char *append_file_to_path (const char *path, const char *file)
{
unsigned path_length = strlen(path);
unsigned file_length = strlen(file);
unsigned total_length = path_length + 1 + file_length;
char *result;
if (path_length == 0)
{
/* return strdup(file); */
result = NSZoneMalloc(NSDefaultMallocZone(),
sizeof(char) * file_length + 1);
memcpy(result, file, sizeof(char) * file_length);
result[file_length] = '\0';
return result;
}
result = NSZoneMalloc(NSDefaultMallocZone(),
sizeof(char) * total_length + 1);
memcpy(result, path, sizeof(char) * path_length);
#ifdef __MINGW__
result[path_length] = '\\';
#else
result[path_length] = '/';
#endif
memcpy(&result[path_length + 1], file, sizeof(char) * file_length);
result[total_length] = '\0';
return result;
}
static SEL swfsSel = 0;
@implementation NSDirectoryEnumerator @implementation NSDirectoryEnumerator
// Implementation dependent methods + (void) initialize
/*
recurses into directory `path'
- pushes relative path (relative to root of search) on _pathStack
- pushes system dir enumerator on enumPath
*/
- (void) recurseIntoDirectory: (NSString*)path relativeName: (NSString*)name
{ {
const char* cpath; if (self == [NSDirectoryEnumerator class])
DIR* dir;
cpath = [[NSFileManager defaultManager]
fileSystemRepresentationWithPath: path];
dir = opendir(cpath);
if (dir)
{ {
[_pathStack addObject: name]; /* Initialize the default manager which we access directly */
[_enumStack addObject: [NSValue valueWithPointer: dir]]; [NSFileManager defaultManager];
} swfsSel = @selector(stringWithFileSystemRepresentation:length:);
else
NSLog(@"Failed to recurse into directory '%@' - %s",
path, strerror(errno));
}
/*
backtracks enumeration to the previous dir
- pops current dir relative path from _pathStack
- pops system dir enumerator from _enumStack
- sets currentFile* to nil
*/
- (void) backtrack
{
closedir((DIR*)[[_enumStack lastObject] pointerValue]);
[_enumStack removeLastObject];
[_pathStack removeLastObject];
DESTROY(_currentFileName);
DESTROY(_currentFilePath);
DESTROY(_fileAttributes);
}
/*
finds the next file according to the top enumerator
- if there is a next file it is put in currentFile
- if the current file is a directory and if isRecursive calls
recurseIntoDirectory: currentFile
- if the current file is a symlink to a directory and if isRecursive
and isFollowing calls recurseIntoDirectory: currentFile
- if at end of current directory pops stack and attempts to
find the next entry in the parent
- sets currentFile to nil if there are no more files to enumerate
*/
- (void) findNextFile
{
NSFileManager* manager = [NSFileManager defaultManager];
DIR_enum_state* dir;
DIR_enum_item* dirbuf;
struct stat statbuf;
const char* cpath;
DESTROY(_currentFileName);
DESTROY(_currentFilePath);
DESTROY(_fileAttributes);
while ([_pathStack count])
{
dir = (DIR*)[[_enumStack lastObject] pointerValue];
dirbuf = readdir(dir);
if (dirbuf)
{
/* Skip "." and ".." directory entries */
if (strcmp(dirbuf->d_name, ".") == 0 ||
strcmp(dirbuf->d_name, "..") == 0)
continue;
// Name of current file
_currentFileName = [manager
stringWithFileSystemRepresentation: dirbuf->d_name
length: strlen(dirbuf->d_name)];
_currentFileName = RETAIN([[_pathStack lastObject]
stringByAppendingPathComponent: _currentFileName]);
// Full path of current file
_currentFilePath = RETAIN([_topPath
stringByAppendingPathComponent: _currentFileName]);
// Check if directory
cpath = [manager fileSystemRepresentationWithPath: _currentFilePath];
if (_flags.isRecursive == YES)
{
// Do not follow links
#ifdef S_IFLNK
if (!_flags.isFollowing)
{
if (lstat(cpath, &statbuf) != 0)
break;
// If link then return it as link
if (S_IFLNK == (S_IFMT & statbuf.st_mode))
break;
}
else
#endif
{
if (stat(cpath, &statbuf) != 0)
break;
}
if (S_IFDIR == (S_IFMT & statbuf.st_mode))
{
[self recurseIntoDirectory: _currentFilePath
relativeName: _currentFileName];
}
}
break; // Got a file name - break out of loop
}
else
{
[self backtrack];
}
} }
} }
@ -1426,31 +1379,57 @@ static NSFileManager* defaultManager = nil;
- (id) initWithDirectoryPath: (NSString*)path - (id) initWithDirectoryPath: (NSString*)path
recurseIntoSubdirectories: (BOOL)recurse recurseIntoSubdirectories: (BOOL)recurse
followSymlinks: (BOOL)follow followSymlinks: (BOOL)follow
prefixFiles: (BOOL)prefix justContents: (BOOL)justContents
{ {
_pathStack = [NSMutableArray new]; DIR* dir_pointer;
_enumStack = [NSMutableArray new]; const char *topPath;
_stringWithFileSysImp = (NSString *(*)(id, SEL, char *, unsigned))
[defaultManager methodForSelector: swfsSel];
_stack = NSZoneMalloc([self zone], sizeof(GSIArray_t));
GSIArrayInitWithZoneAndCapacity(_stack, [self zone], 64);
_flags.isRecursive = recurse; _flags.isRecursive = recurse;
_flags.isFollowing = follow; _flags.isFollowing = follow;
_flags.justContents = justContents;
topPath = [defaultManager fileSystemRepresentationWithPath: path];
_top_path = NSZoneMalloc(NSDefaultMallocZone(),
sizeof(char) * (strlen(topPath) + 1));
memcpy(_top_path, topPath, sizeof(char) * (strlen(topPath) + 1));
dir_pointer = opendir(_top_path);
if (dir_pointer)
{
GSIArrayItem item;
/* item.ext.path = strdup(""); */
item.ext.path = NSZoneMalloc(NSDefaultMallocZone(), sizeof(char) * 2);
memcpy(item.ext.path, "", sizeof(char) * 2);
item.ext.pointer = dir_pointer;
GSIArrayAddItem(_stack, item);
}
else
{
NSLog(@"Failed to recurse into directory '%@' - %s",
path, strerror(errno));
}
_topPath = RETAIN(path);
[self recurseIntoDirectory: path relativeName: @""];
return self; return self;
} }
- (void) dealloc - (void) dealloc
{ {
while ([_pathStack count]) GSIArrayEmpty(_stack);
[self backtrack]; NSZoneFree([self zone], _stack);
NSZoneFree(NSDefaultMallocZone(), _top_path);
RELEASE(_pathStack); if (_current_file_path != NULL)
RELEASE(_enumStack); {
RELEASE(_topPath); NSZoneFree(NSDefaultMallocZone(), _current_file_path);
TEST_RELEASE(_currentFileName); }
TEST_RELEASE(_currentFilePath);
TEST_RELEASE(_fileAttributes);
TEST_RELEASE(_directoryAttributes);
[super dealloc]; [super dealloc];
} }
@ -1458,42 +1437,150 @@ static NSFileManager* defaultManager = nil;
- (NSDictionary*) directoryAttributes - (NSDictionary*) directoryAttributes
{ {
if (_directoryAttributes == nil) NSString *topPath;
{
_directoryAttributes = [[NSFileManager defaultManager] topPath = _stringWithFileSysImp(defaultManager, swfsSel, _top_path,
fileAttributesAtPath: _topPath strlen(_top_path));
traverseLink: _flags.isFollowing];
IF_NO_GC(RETAIN(_directoryAttributes)); return [defaultManager fileAttributesAtPath: topPath
} traverseLink: _flags.isFollowing];
return _directoryAttributes;
} }
- (NSDictionary*) fileAttributes - (NSDictionary*) fileAttributes
{ {
if (_fileAttributes == nil) NSString *currentFilePath;
{
_fileAttributes = [[NSFileManager defaultManager] currentFilePath = _stringWithFileSysImp(defaultManager, swfsSel,
fileAttributesAtPath: _currentFilePath _current_file_path,
traverseLink: _flags.isFollowing]; strlen(_current_file_path));
IF_NO_GC(RETAIN(_fileAttributes));
} return [defaultManager fileAttributesAtPath: currentFilePath
return _fileAttributes; traverseLink: _flags.isFollowing];
} }
// Skipping subdirectories // Skipping subdirectories
- (void) skipDescendents - (void) skipDescendents
{ {
if ([_pathStack count]) if (GSIArrayCount(_stack) > 0)
[self backtrack]; {
GSIArrayRemoveLastItem(_stack);
if (_current_file_path != NULL)
{
NSZoneFree(NSDefaultMallocZone(), _current_file_path);
_current_file_path = NULL;
}
}
} }
// Enumerate next // Enumerate next
- (id) nextObject - (id) nextObject
{ {
[self findNextFile]; /*
return _currentFileName; finds the next file according to the top enumerator
- if there is a next file it is put in currentFile
- if the current file is a directory and if isRecursive calls
recurseIntoDirectory: currentFile
- if the current file is a symlink to a directory and if isRecursive
and isFollowing calls recurseIntoDirectory: currentFile
- if at end of current directory pops stack and attempts to
find the next entry in the parent
- sets currentFile to nil if there are no more files to enumerate
*/
struct dirent *dirbuf;
struct stat statbuf;
char *current_file_name = NULL;
if (_current_file_path != NULL)
{
NSZoneFree(NSDefaultMallocZone(), _current_file_path);
_current_file_path = NULL;
}
while (GSIArrayCount(_stack) > 0)
{
GSEnumeratedDirectory dir = GSIArrayLastItem(_stack).ext;
dirbuf = readdir(dir.pointer);
if (dirbuf)
{
/* Skip "." and ".." directory entries */
if (strcmp(dirbuf->d_name, ".") == 0
|| strcmp(dirbuf->d_name, "..") == 0)
continue;
// Name of current file
current_file_name = append_file_to_path(dir.path, dirbuf->d_name);
/* TODO - can this one can be removed ? */
if (!_flags.justContents)
{
_current_file_path = append_file_to_path(_top_path,
current_file_name);
}
if (_flags.isRecursive == YES)
{
// Do not follow links
#ifdef S_IFLNK
if (!_flags.isFollowing)
{
if (lstat(_current_file_path, &statbuf) != 0)
break;
// If link then return it as link
if (S_IFLNK == (S_IFMT & statbuf.st_mode))
break;
}
else
#endif
{
if (stat(_current_file_path, &statbuf) != 0)
break;
}
if (S_IFDIR == (S_IFMT & statbuf.st_mode))
{
DIR* dir_pointer;
dir_pointer = opendir(_current_file_path);
if (dir_pointer)
{
GSIArrayItem item;
item.ext.path = current_file_name;
item.ext.pointer = dir_pointer;
GSIArrayAddItem(_stack, item);
}
else
{
NSLog(@"Failed to recurse into directory '%s' - %s",
_current_file_path, strerror(errno));
}
}
}
break; // Got a file name - break out of loop
}
else
{
GSIArrayRemoveLastItem(_stack);
if (_current_file_path != NULL)
{
NSZoneFree(NSDefaultMallocZone(), _current_file_path);
_current_file_path = NULL;
}
}
}
if (current_file_name == NULL)
{
return nil;
}
else
{
return _stringWithFileSysImp(defaultManager, swfsSel,
current_file_name,
strlen(current_file_name));
}
} }
@end /* NSDirectoryEnumerator */ @end /* NSDirectoryEnumerator */
@ -1501,10 +1588,15 @@ static NSFileManager* defaultManager = nil;
@implementation NSDirectoryEnumerator (PrivateMethods) @implementation NSDirectoryEnumerator (PrivateMethods)
- (NSDictionary*) _attributesForCopy - (NSDictionary*) _attributesForCopy
{ {
return [[NSFileManager defaultManager] NSString *currentFilePath;
_attributesAtPath: _currentFilePath
traverseLink: _flags.isFollowing currentFilePath = _stringWithFileSysImp(defaultManager, swfsSel,
forCopy: YES]; _current_file_path,
strlen(_current_file_path));
return [defaultManager _attributesAtPath: currentFilePath
traverseLink: _flags.isFollowing
forCopy: YES];
} }
@end @end