ডেটাতে / থেকে রাউন্ড ট্রিপ সুইফ্ট নম্বর প্রকার


97

সুইফট 3 এর Dataপরিবর্তে ঝুঁকির সাথে [UInt8], আমি এনকোড / ডিকোড করার সবচেয়ে কার্যকরী / আইডোমেটিক পদ্ধতিতে ডেটা অবজেক্ট হিসাবে বিভিন্ন সংখ্যার প্রকার (ইউআইএনটি 8, ডাবল, ফ্লোট, ইনট 64 ইত্যাদি) সন্ধান করার চেষ্টা করছি।

আছে [UInt8] ব্যবহার করার জন্য এই উত্তর , কিন্তু এটা বিভিন্ন পয়েন্টার API গুলি ব্যবহার করা যে আমি ডেটার উপর খুঁজে পাচ্ছি না বলে মনে হয়।

আমি মূলত এমন কিছু কাস্টম এক্সটেনশন দেখতে চাই যা দেখতে এমন কিছু দেখায়:

let input = 42.13 // implicit Double
let bytes = input.data
let roundtrip = bytes.to(Double) // --> 42.13

যে অংশটি আমাকে সত্যিই বাদ দেয়, আমি দস্তাবেজের একটি গুচ্ছ দিয়ে দেখেছি, আমি কীভাবে কোনও মৌলিক কাঠামো (যা সংখ্যার সবগুলিই) থেকে কিছু ধরণের পয়েন্টার জিনিস (ওপেকপয়েন্টার বা বাফারপয়েন্টার বা আনসেক পয়েন্টার?) পেতে পারি। সি-তে, আমি কেবল তার সামনে একটি অ্যাম্পারস্যান্ড চড় মারব এবং সেখানে যাব।


উত্তর:


262

দ্রষ্টব্য: কোডটি এখন সুইফ্ট 5 (এক্সকোড 10.2) এর জন্য আপডেট করা হয়েছে । (সম্পাদনা ইতিহাসে সুইফ্ট 3 এবং সুইফ্ট 4.2 সংস্করণগুলি পাওয়া যাবে)) সম্ভবত স্বাক্ষরবিহীন ডেটা এখন সঠিকভাবে পরিচালিত হয়েছে।

কিভাবে Dataএকটি মান থেকে তৈরি করতে

সুইফট ৪.২ অনুসারে, কেবলমাত্র একটি মান থেকে ডেটা তৈরি করা যায়

let value = 42.13
let data = withUnsafeBytes(of: value) { Data($0) }

print(data as NSData) // <713d0ad7 a3104540>

ব্যাখ্যা:

  • withUnsafeBytes(of: value) মানের কাঁচা বাইটগুলি coveringেকে একটি বাফার পয়েন্টার সহ বন্ধের অনুরোধ জানায়।
  • একটি কাঁচা বাফার পয়েন্টার বাইটের ক্রম, তাই Data($0)ডেটা তৈরি করতে ব্যবহার করা যেতে পারে।

কীভাবে একটি মান পুনরুদ্ধার করবেন Data

সুইফট 5, এর মতো withUnsafeBytes(_:)এর Dataপূজা একটি "untyped" সঙ্গে অবসান UnsafeMutableRawBufferPointerবাইট। load(fromByteOffset:as:)পদ্ধতি স্মৃতি থেকে মান লেখা আছে:

let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
let value = data.withUnsafeBytes {
    $0.load(as: Double.self)
}
print(value) // 42.13

এই পদ্ধতির সাথে একটি সমস্যা রয়েছে: এটির জন্য প্রয়োজন যে মেমরিটি এমন ধরণের জন্য প্রান্তিক সম্পত্তি (এখানে: একটি 8-বাইট ঠিকানায় প্রান্তিক)। তবে এটি গ্যারান্টিযুক্ত নয়, উদাহরণস্বরূপ যদি ডেটা অন্য কোনও Dataমানের টুকরা হিসাবে প্রাপ্ত হয়েছিল obtained

বাইটসটি মানটিতে অনুলিপি করা তাই নিরাপদ :

let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
var value = 0.0
let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(bytesCopied == MemoryLayout.size(ofValue: value))
print(value) // 42.13

ব্যাখ্যা:

  • withUnsafeMutableBytes(of:_:) মানটির কাঁচা বাইটগুলি coveringেকে একটি পরিবর্তনীয় বাফার পয়েন্টার সহ বন্ধের অনুরোধ জানায়।
  • copyBytes(to:)পদ্ধতি DataProtocol(যা Dataকপি কনর্ফাম) যে বাফার তথ্য থেকে বাইট।

এর রিটার্ন মান copyBytes()হ'ল অনুলিপি করা বাইটের সংখ্যা। এটি গন্তব্য বাফারের আকারের সমান বা ডেটাতে পর্যাপ্ত বাইট না থাকলে কম।

জেনেরিক সমাধান # 1

উপরের রূপান্তরগুলি এখন এর জেনেরিক পদ্ধতি হিসাবে সহজেই প্রয়োগ করা যেতে পারে struct Data:

extension Data {

    init<T>(from value: T) {
        self = Swift.withUnsafeBytes(of: value) { Data($0) }
    }

    func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
        var value: T = 0
        guard count >= MemoryLayout.size(ofValue: value) else { return nil }
        _ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
        return value
    }
}

সীমাবদ্ধতা T: ExpressibleByIntegerLiteralএখানে যুক্ত করা হয়েছে যাতে আমরা সহজেই মানটিকে "শূন্য" তে আরম্ভ করতে পারি - এটি আসলেই কোনও বিধিনিষেধ নয় কারণ এই পদ্ধতিটি "ট্রিভাল" (পূর্ণসংখ্যার এবং ভাসমান পয়েন্ট) প্রকারের সাথেই ব্যবহার করা যেতে পারে, নীচে দেখুন।

উদাহরণ:

let value = 42.13 // implicit Double
let data = Data(from: value)
print(data as NSData) // <713d0ad7 a3104540>

if let roundtrip = data.to(type: Double.self) {
    print(roundtrip) // 42.13
} else {
    print("not enough data")
}

একইভাবে, আপনি রূপান্তর করতে পারেন অ্যারে থেকে Dataএবং ফিরে:

extension Data {

    init<T>(fromArray values: [T]) {
        self = values.withUnsafeBytes { Data($0) }
    }

    func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
        var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
        _ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
        return array
    }
}

উদাহরণ:

let value: [Int16] = [1, Int16.max, Int16.min]
let data = Data(fromArray: value)
print(data as NSData) // <0100ff7f 0080>

let roundtrip = data.toArray(type: Int16.self)
print(roundtrip) // [1, 32767, -32768]

জেনেরিক সমাধান # 2

উপরোক্ত পদ্ধতির একটি অসুবিধা রয়েছে: এটি আসলে কেবলমাত্র "তুচ্ছ" ধরণের সাথে পূর্ণসংখ্যার এবং ভাসমান পয়েন্টের ধরণের সাথে কাজ করে। "জটিল" ধরণের পছন্দArrayString অন্তর্নিহিত স্টোরেজটির এবং (গোপন) পয়েন্টারগুলি থাকে এবং কেবল স্ট্রাক্টটি অনুলিপি করেই পাস করা যায় না। এটি এমন রেফারেন্স ধরণের সাথেও কাজ করবে না যা আসল অবজেক্ট স্টোরেজের কেবল পয়েন্টার।

সুতরাং যে সমস্যাটি সমাধান করুন, একটি পারেন

  • এমন একটি প্রোটোকল সংজ্ঞায়িত করুন যা রূপান্তর Dataও পিছনে রূপান্তর করার জন্য পদ্ধতিগুলি সংজ্ঞায়িত করে :

    protocol DataConvertible {
        init?(data: Data)
        var data: Data { get }
    }
    
  • প্রোটোকল এক্সটেনশনে রূপান্তরগুলি ডিফল্ট পদ্ধতি হিসাবে প্রয়োগ করুন:

    extension DataConvertible where Self: ExpressibleByIntegerLiteral{
    
        init?(data: Data) {
            var value: Self = 0
            guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
            _ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
            self = value
        }
    
        var data: Data {
            return withUnsafeBytes(of: self) { Data($0) }
        }
    }
    

    আমি এখানে একটি অনুপযুক্ত আরম্ভকারী বেছে নিয়েছি যা পরীক্ষিত বাইটের সংখ্যাটি আকারের সাথে মেলে cks

  • এবং পরিশেষে সমস্ত ধরণের কনফারেন্স ঘোষণা করুন যা নিরাপদে Dataএবং ফিরে রূপান্তরিত হতে পারে :

    extension Int : DataConvertible { }
    extension Float : DataConvertible { }
    extension Double : DataConvertible { }
    // add more types here ...
    

এটি রূপান্তরকে আরও মার্জিত করে তোলে:

let value = 42.13
let data = value.data
print(data as NSData) // <713d0ad7 a3104540>

if let roundtrip = Double(data: data) {
    print(roundtrip) // 42.13
}

দ্বিতীয় পদ্ধতির সুবিধা হ'ল আপনি অজান্তেই অনিরাপদ রূপান্তর করতে পারবেন না। অসুবিধাটি হ'ল আপনাকে পরিষ্কারভাবে সমস্ত "নিরাপদ" প্রকারের তালিকাবদ্ধ করতে হবে।

আপনি অন্যান্য ধরণের প্রোটোকলও প্রয়োগ করতে পারেন যার জন্য একটি তুচ্ছ-রূপান্তরকরণ প্রয়োজন, যেমন:

extension String: DataConvertible {
    init?(data: Data) {
        self.init(data: data, encoding: .utf8)
    }
    var data: Data {
        // Note: a conversion to UTF-8 cannot fail.
        return Data(self.utf8)
    }
}

বা রূপান্তর পদ্ধতিগুলি নিজের প্রকারে রূপান্তর পদ্ধতিগুলি যা প্রয়োজন তা করার জন্য বাস্তবায়িত করুন যাতে সিরিয়ালাইজ করুন এবং একটি মানকে ডিসরিয়ালাইজ করুন।

বাইট অর্ডার

উপরের পদ্ধতিগুলিতে কোনও বাইট অর্ডার রূপান্তর হয় না, ডেটা সর্বদা হোস্ট বাইট ক্রমে থাকে। প্ল্যাটফর্মের স্বতন্ত্র উপস্থাপনের জন্য (যেমন "বিগ এন্ডিয়ান" ওরফে "নেটওয়ার্ক" বাইট ক্রম), সম্পর্কিত পূর্ণসংখ্যার বৈশিষ্ট্যগুলি ব্যবহার করুন। প্রারম্ভিক। উদাহরণ স্বরূপ:

let value = 1000
let data = value.bigEndian.data
print(data as NSData) // <00000000 000003e8>

if let roundtrip = Int(data: data) {
    print(Int(bigEndian: roundtrip)) // 1000
}

জেনেরিক রূপান্তর পদ্ধতিতে অবশ্যই এই রূপান্তরটি সাধারণত করা যায়।


varআমাদের প্রাথমিক মানটির একটি অনুলিপি তৈরি করতে হবে তার অর্থ কি আমরা বাইটগুলি দুবার অনুলিপি করছি? আমার বর্তমান ব্যবহারের ক্ষেত্রে, আমি এগুলিকে ডেটা স্ট্রকেটে পরিণত করছি, তাই আমি appendতাদের বাইটের ক্রমবর্ধমান প্রবাহে রাখতে পারি। সোজা সি এ, এটি হিসাবে সহজ *(cPointer + offset) = originalValue। সুতরাং বাইটগুলি একবারে অনুলিপি করা হয়।
ট্র্যাভিস গ্রিগস

4
@ ট্র্যাভিসগ্রিগস: কোনও ইনট বা ফ্লোট অনুলিপি করা সম্ভবত প্রাসঙ্গিক নয় তবে আপনি সুইফটে একই জিনিস করতে পারেন । আপনার যদি একটি থাকে ptr: UnsafeMutablePointer<UInt8>তবে আপনি রেফারেন্সড মেমোরিটিকে এমন কোনও কিছুর মাধ্যমে অর্পণ করতে পারেন UnsafeMutablePointer<T>(ptr + offset).pointee = valueযা আপনার সুইফট কোডের সাথে ঘনিষ্ঠভাবে মিল রাখে। একটি সম্ভাব্য সমস্যা রয়েছে: কিছু প্রসেসর কেবল প্রান্তিক মেমরি অ্যাক্সেসের অনুমতি দেয় , উদাহরণস্বরূপ আপনি কোনও বিজোড় মেমরির স্থানে কোনও ইন্টার সংরক্ষণ করতে পারবেন না। এটি বর্তমানে ব্যবহৃত ইন্টেল এবং এআরএম প্রসেসরের ক্ষেত্রে প্রযোজ্য কিনা তা আমি জানি না।
মার্টিন আর

4
@ ট্র্যাভিসগ্রিগস: (চালিয়ে যাওয়া) ... এছাড়াও এর জন্য প্রয়োজন যে একটি বৃহত ডেটা অবজেক্ট ইতিমধ্যে তৈরি করা হয়েছে, এবং সুইফটে আপনি কেবলমাত্র ডেটা অবজেক্টটি তৈরি এবং আরম্ভ করতে পারবেন , সুতরাং আপনার কাছে শূন্য বাইটের অতিরিক্ত কপি থাকতে পারে আরম্ভ. - আপনার যদি আরও বিশদ প্রয়োজন হয় তবে আমি আপনাকে একটি নতুন প্রশ্ন পোস্ট করার পরামর্শ দিচ্ছি।
মার্টিন আর

4
@ হ্যান্স ব্রেন্ডে: আমি আশঙ্কা করছি যে বর্তমানে এটি সম্ভব নয়। এটি একটি প্রয়োজন হবে extension Array: DataConvertible where Element: DataConvertible। এটি সুইফ্ট 3 এ সম্ভব নয়, তবে সুইফট 4 এর জন্য পরিকল্পনা করা হয়েছে (যতদূর আমি জানি)। Github.com/apple/swift/blob/master/docs/…
মার্টিন আর

4
@ মি_কাটসিফারাকিস: আপনি কি ভুল টাইপ Int.selfকরে থাকতে পারেন Int.Type?
মার্টিন আর

3

আপনি ব্যবহার করে পরিবর্তনীয় জিনিসগুলিতে একটি অনিরাপদ পয়েন্টার পেতে পারেন withUnsafePointer:

withUnsafePointer(&input) { /* $0 is your pointer */ }

অপরিবর্তনীয় বস্তুর জন্য একটি পাওয়ার উপায় সম্পর্কে আমি জানি না, কারণ ইনআউট অপারেটর কেবল পরিবর্তনীয় অবজেক্টগুলিতে কাজ করে।

আপনি যে উত্তরটির সাথে লিঙ্ক করেছেন এটি এটি প্রদর্শিত হয়।


2

আমার ক্ষেত্রে, মার্টিন আর এর উত্তর সাহায্য করেছে কিন্তু ফলাফলটি উল্টে গেছে। সুতরাং আমি তার কোডে একটি ছোট পরিবর্তন করেছি:

extension UInt16 : DataConvertible {

    init?(data: Data) {
        guard data.count == MemoryLayout<UInt16>.size else { 
          return nil 
        }
    self = data.withUnsafeBytes { $0.pointee }
    }

    var data: Data {
         var value = CFSwapInt16HostToBig(self)//Acho que o padrao do IOS 'e LittleEndian, pois os bytes estavao ao contrario
         return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
    }
}

সমস্যাটি লিটল ইন্ডিয়ান এবং বিগেনডিয়ান সম্পর্কিত।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.