Skip to content

Commit 11f5bf5

Browse files
committed
Fix conversion of BIT fields (fixes #2254)
1 parent c5878bd commit 11f5bf5

File tree

2 files changed

+84
-14
lines changed

2 files changed

+84
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// DataConversion_Tests.m
3+
// SPMySQLFramework
4+
//
5+
// Created by Max Lohrmann on 01.10.15.
6+
// Copyright (c) 2015 Max Lohrmann. All rights reserved.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the "Software"), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
// More info at <https://github.com/sequelpro/sequelpro>
30+
31+
@interface DataConversion_Tests : XCTestCase
32+
33+
- (void)test_bitStringWithBytes;
34+
35+
@end
36+
37+
@implementation DataConversion_Tests
38+
39+
- (void)test_bitStringWithBytes
40+
{
41+
// BIT(1)
42+
{
43+
unsigned char y = 1;
44+
unsigned char n = 0;
45+
XCTAssertEqualObjects(_bitStringWithBytes(&y,sizeof(y),1), @"1");
46+
XCTAssertEqualObjects(_bitStringWithBytes(&n,sizeof(n),0), @"0");
47+
}
48+
// BIT(3)
49+
{
50+
const char input[] = {5};
51+
NSUInteger bitSize = 3;
52+
NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize);
53+
XCTAssertEqualObjects(res, @"101");
54+
}
55+
// BIT(16)
56+
{
57+
const char input[] = {0xcc,0xf0};
58+
NSUInteger bitSize = 16;
59+
NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize);
60+
XCTAssertEqualObjects(res, @"1100110011110000");
61+
}
62+
// BIT(20)
63+
{
64+
const char input[] = {0x0f,0xcc,0xf0};
65+
NSUInteger bitSize = 20;
66+
NSString *res = _bitStringWithBytes(input,sizeof(input),bitSize);
67+
XCTAssertEqualObjects(res, @"11111100110011110000");
68+
}
69+
}
70+
71+
@end

Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Data Conversion.m

+13-14
Original file line numberDiff line numberDiff line change
@@ -209,27 +209,26 @@ static inline SPMySQLResultFieldProcessor _processorForField(MYSQL_FIELD aField)
209209
return nil;
210210
}
211211

212-
// Ensure padLength is never lower than the length
213-
if (padLength < bitLength) {
214-
padLength = bitLength;
215-
}
216-
212+
// use whatever is smaller. padLength comes from BIT(x), bitLength from the actual bytes transmitted.
213+
// if bitLength < padLength it means the value is smaller than what the field can accomodate.
214+
// if bitLength > padLength it means BIT(x) is not a full n bytes long and was extended by mysqls storage.
215+
// In that case the additional bits should still be 0 as mysql does not allow to set bits over the size of x.
216+
bitLength = MIN(bitLength,padLength);
217217
// Generate a nul-terminated C string representation of the binary data
218218
char *cStringBuffer = malloc(padLength + 1);
219-
cStringBuffer[padLength] = '\0';
219+
memset(cStringBuffer, '0', padLength);
220220

221221
while (i < bitLength)
222222
{
223+
// start with the least significant bit (the rightmost bit in the last byte) and move left
224+
unsigned char bitInByteMask = i % 8; // 0-7, the cycle is 0,1,...,7,0,...
225+
unsigned long bytesOffset = (length - 1) - (i >> 3); // i>>3 == floor(i/8)
223226
++i;
224-
225-
cStringBuffer[padLength - i] = ((bytes[length - 1 - (i >> 3)] >> (i & 0x7)) & 1 ) ? '1' : '0';
227+
cStringBuffer[padLength - i] = ((bytes[bytesOffset] & (1 << bitInByteMask)) != 0) ? '1' : '0';
226228
}
227-
228-
while (i++ < padLength)
229-
{
230-
cStringBuffer[padLength - i] = '0';
231-
}
232-
229+
230+
cStringBuffer[padLength] = '\0';
231+
233232
// Convert to a string
234233
NSString *returnString = [NSString stringWithUTF8String:cStringBuffer];
235234

0 commit comments

Comments
 (0)