I am writing code to read and write file using a rigidly defined binary format. I have a mixture of Int16 and Int32 values, which are defined as a 240 byte structure. How do I concatenate these values into this single structure?
here is an example, you can custom your own init method, use loop if there two many repeated length
public struct HistoryRecord {
public let sequence: UInt8
let impFlag: UInt8
let mode: UInt8
let power: UInt16
let utc: UInt32
let weight: UInt16
let impedance: UInt16
private let sequenceLength = 1
private let modeLength = 1
private let powerLength = 2
private let utcLength = 4
private let weightLength = 2
private let impedanceLength = 2
public init() {
sequence = 0
impFlag = 0
mode = 0
power = 0
utc = 0
weight = 0
impedance = 0
}
public init?(_ data: NSData) {
let needLength = sequenceLength + modeLength + powerLength + utcLength + weightLength + impedanceLength
guard data.length == needLength else {
return nil
}
var bufferUInt8: UInt8
var bufferUInt16: UInt16
var bufferUInt32: UInt32
bufferUInt8 = 0
data.getBytes(&bufferUInt8, range: NSMakeRange(0, sequenceLength))
sequence = bufferUInt8
bufferUInt8 = 0
data.getBytes(&bufferUInt8, range: NSMakeRange(sequenceLength, modeLength))
impFlag = (bufferUInt8 >> 4) & 0x00ff
mode = bufferUInt8 & 0x00ff
bufferUInt16 = 0
data.getBytes(&bufferUInt16, range: NSMakeRange(sequenceLength + modeLength, powerLength))
power = bufferUInt16.bigEndian
bufferUInt32 = 0
data.getBytes(&bufferUInt32, range: NSMakeRange(sequenceLength + modeLength + powerLength, utcLength))
utc = bufferUInt32.bigEndian
bufferUInt16 = 0
data.getBytes(&bufferUInt16, range: NSMakeRange(sequenceLength + modeLength + powerLength + utcLength, weightLength))
weight = bufferUInt16.bigEndian
bufferUInt16 = 0
data.getBytes(&bufferUInt16, range: NSMakeRange(sequenceLength + modeLength + powerLength + utcLength + weightLength, impedanceLength))
impedance = bufferUInt16.bigEndian
Related
I need 16383 to be converted to 7F7F but I can only get this to be converted to 3fff or 77377.
I can convert 8192 to hexadecimal string 4000 which is essentially the same thing.
If I use let firstHexa = String(format:"%02X", a) It stops at 3fff hexadecimal for the first number and and 2000 hexadecimal for the second number. here is my code
public func intToHexString(_ int: Int16) -> String {
var encodedHexa: String = ""
if int >= -8192 && int <= 8191 {
let int16 = int + 8192
//convert to two unsigned Int8 bytes
let a = UInt8(int16 >> 8 & 0x00ff)
let b = UInt8(int16 & 0x00ff)
//convert the 2 bytes to hexadecimals
let first1Hexa = String(a, radix: 8 )
let second2Hexa = String(b, radix: 8)
let firstHexa = String(format:"%02X", a)
let secondHexa = String(format:"%02X", b)
//combine the 2 hexas into 1 string with 4 characters...adding 0 to the beggining if only 1 character.
if firstHexa.count == 1 {
let appendedFHexa = "0" + firstHexa
encodedHexa = appendedFHexa + secondHexa
} else if secondHexa.count == 1 {
let appendedSHexa = "0" + secondHexa
encodedHexa = firstHexa + appendedSHexa
} else {
encodedHexa = firstHexa + secondHexa
}
}
return encodedHexa
}
Please help ma'ams and sirs! Thanks.
From your test cases, it seems like your values are 7 bits per byte.
You want 8192 to convert to 4000.
You want 16383 to convert to 7F7F.
Note that:
(0x7f << 7) + 0x7f == 16383
Given that:
let a = UInt8((int16 >> 7) & 0x7f)
let b = UInt8(int16 & 0x7f)
let result = String(format: "%02X%02X", a , b)
This gives:
"4000" for 8128
"7F7F" for 16383
To reverse the process:
let str = "7F7F"
let value = Int(str, radix: 16)!
let result = ((value >> 8) & 0x7f) << 7 + (value & 0x7f)
print(result) // 16383
This is my loop code:
let entyLimitLength = 256 // word enty must smaller than 256
let sizeOfWordDataOffset = 4 // 4 bytes or 8 byte if 64 bit
let sizeOfWordDataSize = 4 //4 bytes or 8 byte if 64 bit
var entyLocation = 0
var entyLength = 0
var searchRange:NSRange
var subDataEnty:NSData
var wordDataOffset:UInt32 = 0
var wordDataSize:UInt32 = 0
var rslt = [(String,NSData)]()
while entyLocation < idxData.length{
searchRange = idxData.rangeOfData(nulTerminateCharacter, options: searchOption, range: NSMakeRange(entyLocation, min(entyLimitLength,(idxData.length - entyLocation))))
if searchRange.length == 0 {
break // not match
}
entyLength = searchRange.location - entyLocation
// this is what data sequene we need
subDataEnty = idxData.subdataWithRange(NSMakeRange(entyLocation, entyLength))
//point to offset number 's data sequene
entyLocation = searchRange.location + searchRange.length
//use pointer to get result ##
idxData.getBytes(&wordDataOffset, range: NSMakeRange(entyLocation,sizeOfWordDataOffset))
//swap Big endian to Litter endian (host)
wordDataOffset = CFSwapInt32BigToHost(wordDataOffset)
//point to word data size's data sequene
entyLocation = entyLocation + sizeOfWordDataOffset
//the same for word data size
idxData.getBytes(&wordDataSize, range: NSMakeRange(entyLocation,sizeOfWordDataSize))
wordDataSize = CFSwapInt32BigToHost(wordDataSize)
//point to next data sequene
entyLocation = entyLocation + sizeOfWordDataSize
rslt = rslt + [(NSString(data: subDataEnty, encoding: NSUTF8StringEncoding) as! String , dictData.subdataWithRange(NSMakeRange(Int(wordDataOffset), Int(wordDataSize))))]
}
return rslt
It take NSData form file and change it in to [(String,NSData)] array
This while loop run above 200000 times. In Xcode Simulation it take too much time.I dont think it take too long time as it is . Is there some reason make it slow down ?
and Please dont vote down a newbie question guys.
I have some binary data that encodes a two byte value as a signed integer.
bytes[1] = 255 // 0xFF
bytes[2] = 251 // 0xF1
Decoding
This is fairly easy - I can extract an Int16 value from these bytes with:
Int16(bytes[1]) << 8 | Int16(bytes[2])
Encoding
This is where I'm running into issues. Most of my data spec called for UInt and that is easy but I'm having trouble extracting the two bytes that make up an Int16
let nv : Int16 = -15
UInt8(nv >> 8) // fail
UInt8(nv) // fail
Question
How would I extract the two bytes that make up an Int16 value
You should work with unsigned integers:
let bytes: [UInt8] = [255, 251]
let uInt16Value = UInt16(bytes[0]) << 8 | UInt16(bytes[1])
let uInt8Value0 = uInt16Value >> 8
let uInt8Value1 = UInt8(uInt16Value & 0x00ff)
If you want to convert UInt16 to bit equivalent Int16 then you can do it with specific initializer:
let int16Value: Int16 = -15
let uInt16Value = UInt16(bitPattern: int16Value)
And vice versa:
let uInt16Value: UInt16 = 65000
let int16Value = Int16(bitPattern: uInt16Value)
In your case:
let nv: Int16 = -15
let uNv = UInt16(bitPattern: nv)
UInt8(uNv >> 8)
UInt8(uNv & 0x00ff)
You could use init(truncatingBitPattern: Int16) initializer:
let nv: Int16 = -15
UInt8(truncatingBitPattern: nv >> 8) // -> 255
UInt8(truncatingBitPattern: nv) // -> 241
I would just do this:
let a = UInt8(nv >> 8 & 0x00ff) // 255
let b = UInt8(nv & 0x00ff) // 241
extension Int16 {
var twoBytes : [UInt8] {
let unsignedSelf = UInt16(bitPattern: self)
return [UInt8(truncatingIfNeeded: unsignedSelf >> 8),
UInt8(truncatingIfNeeded: unsignedSelf)]
}
}
var test : Int16 = -15
test.twoBytes // [255, 241]
Hy,
I have a very Basic Question which is :
How can i create a random number with 20 digits no floats no negatives (basically an Int) in Swift ?
Thanks for all answers XD
Step 1
First of all we need an extension of Int to generate a random number in a range.
extension Int {
init(_ range: Range<Int> ) {
let delta = range.startIndex < 0 ? abs(range.startIndex) : 0
let min = UInt32(range.startIndex + delta)
let max = UInt32(range.endIndex + delta)
self.init(Int(min + arc4random_uniform(max - min)) - delta)
}
}
This can be used this way:
Int(0...9) // 4 or 1 or 1...
Int(10...99) // 90 or 33 or 11
Int(100...999) // 200 or 333 or 893
Step 2
Now we need a function that receive the number of digits requested, calculates the range of the random number and finally does invoke the new initializer of Int.
func random(digits:Int) -> Int {
let min = Int(pow(Double(10), Double(digits-1))) - 1
let max = Int(pow(Double(10), Double(digits))) - 1
return Int(min...max)
}
Test
random(1) // 8
random(2) // 12
random(3) // 829
random(4) // 2374
Swift 5: Simple Solution
func random(digits:Int) -> String {
var number = String()
for _ in 1...digits {
number += "\(Int.random(in: 1...9))"
}
return number
}
print(random(digits: 1)) //3
print(random(digits: 2)) //59
print(random(digits: 3)) //926
Note It will return value in String, if you need Int value then you can do like this
let number = Int(random(digits: 1)) ?? 0
Here is some pseudocode that should do what you want.
generateRandomNumber(20)
func generateRandomNumber(int numDigits){
var place = 1
var finalNumber = 0;
for(int i = 0; i < numDigits; i++){
place *= 10
var randomNumber = arc4random_uniform(10)
finalNumber += randomNumber * place
}
return finalNumber
}
Its pretty simple. You generate 20 random numbers, and multiply them by the respective tens, hundredths, thousands... place that they should be on. This way you will guarantee a number of the correct size, but will randomly generate the number that will be used in each place.
Update
As said in the comments you will most likely get an overflow exception with a number this long, so you'll have to be creative in how you'd like to store the number (String, ect...) but I merely wanted to show you a simple way to generate a number with a guaranteed digit length. Also, given the current code there is a small chance your leading number could be 0 so you should protect against that as well.
you can create a string number then convert the number to your required number.
func generateRandomDigits(_ digitNumber: Int) -> String {
var number = ""
for i in 0..<digitNumber {
var randomNumber = arc4random_uniform(10)
while randomNumber == 0 && i == 0 {
randomNumber = arc4random_uniform(10)
}
number += "\(randomNumber)"
}
return number
}
print(Int(generateRandomDigits(3)))
for 20 digit you can use Double instead of Int
Here is 18 decimal digits in a UInt64:
(Swift 3)
let sz: UInt32 = 1000000000
let ms: UInt64 = UInt64(arc4random_uniform(sz))
let ls: UInt64 = UInt64(arc4random_uniform(sz))
let digits: UInt64 = ms * UInt64(sz) + ls
print(String(format:"18 digits: %018llu", digits)) // Print with leading 0s.
16 decimal digits with leading digit 1..9 in a UInt64:
let sz: UInt64 = 100000000
let ld: UInt64 = UInt64(arc4random_uniform(9)+1)
let ms: UInt64 = UInt64(arc4random_uniform(UInt32(sz/10)))
let ls: UInt64 = UInt64(arc4random_uniform(UInt32(sz)))
let digits: UInt64 = ld * (sz*sz/10) + (ms * sz) + ls
print(String(format:"16 digits: %llu", digits))
Swift 3
appzyourlifz's answer updated to Swift 3
Step 1:
extension Int {
init(_ range: Range<Int> ) {
let delta = range.lowerBound < 0 ? abs(range.lowerBound) : 0
let min = UInt32(range.lowerBound + delta)
let max = UInt32(range.upperBound + delta)
self.init(Int(min + arc4random_uniform(max - min)) - delta)
}
}
Step 2:
func randomNumberWith(digits:Int) -> Int {
let min = Int(pow(Double(10), Double(digits-1))) - 1
let max = Int(pow(Double(10), Double(digits))) - 1
return Int(Range(uncheckedBounds: (min, max)))
}
Usage:
randomNumberWith(digits:4) // 2271
randomNumberWith(digits:8) // 65273410
Swift 4 version of Unome's validate response plus :
Guard it against overflow and 0 digit number
Adding support for Linux's device because "arc4random*" functions don't exit
With linux device don't forgot to do
#if os(Linux)
srandom(UInt32(time(nil)))
#endif
only once before calling random.
/// This function generate a random number of type Int with the given digits number
///
/// - Parameter digit: the number of digit
/// - Returns: the ramdom generate number or nil if wrong parameter
func randomNumber(with digit: Int) -> Int? {
guard 0 < digit, digit < 20 else { // 0 digit number don't exist and 20 digit Int are to big
return nil
}
/// The final ramdom generate Int
var finalNumber : Int = 0;
for i in 1...digit {
/// The new generated number which will be add to the final number
var randomOperator : Int = 0
repeat {
#if os(Linux)
randomOperator = Int(random() % 9) * Int(powf(10, Float(i - 1)))
#else
randomOperator = Int(arc4random_uniform(9)) * Int(powf(10, Float(i - 1)))
#endif
} while Double(randomOperator + finalNumber) > Double(Int.max) // Verification to be sure to don't overflow Int max size
finalNumber += randomOperator
}
return finalNumber
}
I receive an Int from my server which I’d like to explode in to an array of bit masks. So for example, if my server gives me the number 3, we get two values, a binary 1 and a binary 2.
How do I do this in Swift?
You could use:
let number = 3
//radix: 2 is binary, if you wanted hex you could do radix: 16
let str = String(number, radix: 2)
println(str)
prints "11"
let number = 79
//radix: 2 is binary, if you wanted hex you could do radix: 16
let str = String(number, radix: 16)
println(str)
prints "4f"
I am not aware of any nice built-in way, but you could use this:
var i = 3
let a = 0..<8
var b = a.map { Int(i & (1 << $0)) }
// b = [1, 2, 0, 0, 0, 0, 0, 0]
Here is a straightforward implementation:
func intToMasks(var n: Int) -> [Int] {
var masks = [Int]()
var mask = 1
while n > 0 {
if n & mask > 0 {
masks.append(mask)
n -= mask
}
mask <<= 1
}
return masks
}
println(intToMasks(3)) // prints "[1,2]"
println(intToMasks(1000)) // prints "[8,32,64,128,256,512]"
public extension UnsignedInteger {
/// The digits that make up this number.
/// - Parameter radix: The base the result will use.
func digits(radix: Self = 10) -> [Self] {
sequence(state: self) { quotient in
guard quotient > 0
else { return nil }
let division = quotient.quotientAndRemainder(dividingBy: radix)
quotient = division.quotient
return division.remainder
}
.reversed()
}
}
let digits = (6 as UInt).digits(radix: 0b10) // [1, 1, 0]
digits.reversed().enumerated().map { $1 << $0 } // [0, 2, 4]
Reverse the result too, if you need it.