From 11f5bf55b3d6ecb9afe3a19d60aa9669bd449e42 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 2 Oct 2015 16:31:39 +0200 Subject: Fix conversion of BIT fields (fixes #2254) --- .../SPMySQL Unit Tests/DataConversion_Tests.m | 71 ++++++++++++++++++++++ .../SPMySQLResult Categories/Data Conversion.m | 27 ++++---- 2 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m (limited to 'Frameworks') diff --git a/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m new file mode 100644 index 00000000..385eb0b2 --- /dev/null +++ b/Frameworks/SPMySQLFramework/SPMySQL Unit Tests/DataConversion_Tests.m @@ -0,0 +1,71 @@ +// +// DataConversion_Tests.m +// SPMySQLFramework +// +// Created by Max Lohrmann on 01.10.15. +// Copyright (c) 2015 Max Lohrmann. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at + +@interface DataConversion_Tests : XCTestCase + +- (void)test_bitStringWithBytes; + +@end + +@implementation DataConversion_Tests + +- (void)test_bitStringWithBytes +{ + // BIT(1) + { + unsigned char y = 1; + unsigned char n = 0; + XCTAssertEqualObjects(_bitStringWithBytes(&y,sizeof(y),1), @"1"); + XCTAssertEqualObjects(_bitStringWithBytes(&n,sizeof(n),0), @"0"); + } + // BIT(3) + { + const char input[] = {5}; + NSUInteger bitSize = 3; + NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize); + XCTAssertEqualObjects(res, @"101"); + } + // BIT(16) + { + const char input[] = {0xcc,0xf0}; + NSUInteger bitSize = 16; + NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize); + XCTAssertEqualObjects(res, @"1100110011110000"); + } + // BIT(20) + { + const char input[] = {0x0f,0xcc,0xf0}; + NSUInteger bitSize = 20; + NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize); + XCTAssertEqualObjects(res, @"11111100110011110000"); + } +} + +@end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m index 639ff0b9..8c5b9181 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m @@ -209,27 +209,26 @@ static inline NSString * _bitStringWithBytes(const char *bytes, NSUInteger lengt return nil; } - // Ensure padLength is never lower than the length - if (padLength < bitLength) { - padLength = bitLength; - } - + // use whatever is smaller. padLength comes from BIT(x), bitLength from the actual bytes transmitted. + // if bitLength < padLength it means the value is smaller than what the field can accomodate. + // if bitLength > padLength it means BIT(x) is not a full n bytes long and was extended by mysqls storage. + // In that case the additional bits should still be 0 as mysql does not allow to set bits over the size of x. + bitLength = MIN(bitLength,padLength); // Generate a nul-terminated C string representation of the binary data char *cStringBuffer = malloc(padLength + 1); - cStringBuffer[padLength] = '\0'; + memset(cStringBuffer, '0', padLength); while (i < bitLength) { + // start with the least significant bit (the rightmost bit in the last byte) and move left + unsigned char bitInByteMask = i % 8; // 0-7, the cycle is 0,1,...,7,0,... + unsigned long bytesOffset = (length - 1) - (i >> 3); // i>>3 == floor(i/8) ++i; - - cStringBuffer[padLength - i] = ((bytes[length - 1 - (i >> 3)] >> (i & 0x7)) & 1 ) ? '1' : '0'; + cStringBuffer[padLength - i] = ((bytes[bytesOffset] & (1 << bitInByteMask)) != 0) ? '1' : '0'; } - - while (i++ < padLength) - { - cStringBuffer[padLength - i] = '0'; - } - + + cStringBuffer[padLength] = '\0'; + // Convert to a string NSString *returnString = [NSString stringWithUTF8String:cStringBuffer]; -- cgit v1.2.3