我正在解密一个视频文件,该文件非常适合小尺寸文件,但对于300mb以上的文件,则存在内存崩溃。代码如下: 我检查了开始字节值,该值上升到315mb,然后崩溃了,我的文件大小为350mb。
它适用于少数iPhone,而崩溃很少。最好的解决方案是分批执行以避免内存问题,但是这样做也会崩溃。
#define kChunkSizeBytes (1024*1024) // 1 MB
@implementation NSMutableData (Crypto)
-(BOOL) doCrypto:(NSString *)key operation: (CCOperation) operation
{
//Keeping it 32 as per our key
char keyPtr[512 + 1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// Fetch key data
if (![key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]) {return FALSE;} // Length of 'key' is bigger than keyPtr
CCCryptorRef cryptor;
CCCryptorStatus cryptStatus = CCCryptorCreate(operation, kCCAlgorithmRC4, 0,
keyPtr, key.length,
NULL, // IV - needed?
&cryptor);
if (cryptStatus != kCCSuccess) { // Handle error here
return FALSE;
}
size_t dataOutMoved;
size_t dataInLength = kChunkSizeBytes; // #define kChunkSizeBytes (16)
size_t dataOutLength = CCCryptorGetOutputLength(cryptor, dataInLength, FALSE);
size_t totalLength = 0; // Keeps track of the total length of the output buffer
size_t filePtr = 0; // Maintains the file pointer for the output buffer
NSInteger startByte; // Maintains the file pointer for the input buffer
char *dataIn = malloc(dataInLength);
char *dataOut = malloc(dataOutLength);
NSRange bytesRange = NSMakeRange((NSUInteger) 0, (NSUInteger) 0);
for (startByte = 0; startByte <= [self length]; startByte += kChunkSizeBytes) {
if ((startByte + kChunkSizeBytes) > [self length]) {
dataInLength = [self length] - startByte;
}
else {
dataInLength = kChunkSizeBytes;
}
// Get the chunk to be ciphered from the input buffer
bytesRange = NSMakeRange((NSUInteger) startByte, (NSUInteger) dataInLength);
[self getBytes:dataIn range:bytesRange];
cryptStatus = CCCryptorUpdate(cryptor, dataIn, dataInLength, dataOut, dataOutLength, &dataOutMoved);
if (startByte >= 203728200) {
NSLog(@"%ld",(long)startByte);
}
if (dataOutMoved != dataOutLength) {
NSLog(@"dataOutMoved (%d) != dataOutLength (%d)", dataOutMoved, dataOutLength);
}
if ( cryptStatus != kCCSuccess)
{
NSLog(@"Failed CCCryptorUpdate: %d", cryptStatus);
}
// Write the ciphered buffer into the output buffer
bytesRange = NSMakeRange(filePtr, (NSUInteger) dataOutMoved);
[self replaceBytesInRange:bytesRange withBytes:dataOut];
totalLength += dataOutMoved;
filePtr += dataOutMoved;
}
// Finalize encryption/decryption.
cryptStatus = CCCryptorFinal(cryptor, dataOut, dataOutLength, &dataOutMoved);
totalLength += dataOutMoved;
if ( cryptStatus != kCCSuccess)
{
NSLog(@"Failed CCCryptorFinal: %d", cryptStatus);
}
// In the case of encryption, expand the buffer if it required some padding (an encrypted buffer will always be a multiple of 16).
// In the case of decryption, truncate our buffer in case the encrypted buffer contained some padding
[self setLength:totalLength];
// Finalize the buffer with data from the CCCryptorFinal call
NSRange bytesNewRange = NSMakeRange(filePtr, (NSUInteger) dataOutMoved);
[self replaceBytesInRange:bytesNewRange withBytes:dataOut];
CCCryptorRelease(cryptor);
free(dataIn);
free(dataOut);
return 1;
}
@end
答案 0 :(得分:0)
如果replaceBytesInRange:bytesRange
导致崩溃,那么我关于如何避免崩溃的第一个建议是为它所依赖的先前函数调用添加错误检查。
例如,在崩溃的情况下,bytesRange
可能不是有效/不可用的值。也许其dataOut
无效/可用。问题中的代码会设置函数调用中的这些值,但不会检查错误条件/错误指示符/无效值的返回值。
它可能是一个相关的依赖函数调用。例如,通过调用cryptStatus
来设置CCCryptorUpdate()
,该调用具有dataOut
作为输入参数。我不了解Objective-C,并且对函数CCCryptorUpdate()
并不熟悉,但是它似乎会影响/填充dataOut
。如果它实际上正在返回错误,那么在您在dataOut
行上使用它时,replaceBytesInRange
可能未处于可用状态。检查cryptStatus
的返回值可能会标记条件,以使您在以后的调用中不应该继续使用dataOut
。
这使我想起另一件事:您做检查了几件事,但只记录了它们。对if (dataOutMoved != dataOutLength)
和(cryptStatus != kCCSuccess)
的检查看起来应该停止执行,或者退出循环,或者类似的检查,而不仅仅是记录发生的情况。
我看到的另一件事是dataOut
被malloc()
一次,没有被清除,并且被重复使用。在某些情况下,这可能是完全有效的,但也可能导致您所看到的错误。您的代码中是否有任何内容假设dataOut
的内容?我正在考虑假定为空终止的C字符串操作。如果不小心,则假定例如整个缓冲区已满,则假定存在的空终止符(或者可能实际上是最初存在的空终止符)会被覆盖。同样,我不太了解Objective-C和这些功能,因此,这并不是专门的声明,而是更多地类似于可能发生的事情的种类。>
TL; DR :向每个函数调用中添加错误检查,并相应地做出响应(中断,退出,重试等),以便以后的函数调用都不会尝试使用指示错误或无效的值。
我敢打赌,通过添加错误检查,您将(1)停止崩溃,并且(2)了解有关为什么的一些特定知识,即文件大于一定数量会导致这些崩溃。