How can I convert a ZipWriter to Bytes in Rust? - post

I want to create a zip from a certain file, then i convert this zip (A buffer, not a file in the disk) to bytes, then to send it by a post body to an api.
Thats the code (simplifyed) just with the essencial, and from now to the next step, how can i convert it into bytes?
pub fn to_zip() {
let buf: &mut [u8] = &mut [0u8; 65536];
let w = std::io::Cursor::new(buf);
let mut zip = zip::ZipWriter::new(w);
// * Convert the buffer to bytes
zip.finish().unwrap();
}
Sorry for a maybe confuse code first time with Rust beeing loving it so far!

zip.finish().unwrap() gives you the Cursor that you used to create the ZipWriter. You can then use into_inner to go back to a &mut [u8].

Related

`web_sys::Url::create_object_url_with_blob(&blob)` not formatting binary data correctly

I have the following code:
let bytes: Vec<u8> = load_file_as_bytes("mydoc.docx"); // This gets a byte vec representation of the file mydoc.docx
let uint8arr = unsafe { js_sys::Uint8Array::view(&bytes) };
let js_value = wasm_bindgen::JsValue::from(uint8arr);
let blob = Blob::new_with_u8_array_sequence_and_options(
&js_value,
web_sys::BlobPropertyBag::new().type_("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
).unwrap();
let download_url = web_sys::Url::create_object_url_with_blob(&blob).unwrap();
When I follow the link, the file that gets downloaded is a bunch of bytes written inside a Word document.
These bytes are meant to represent the word document itself and not written to it as plaintext.
This is being compiled to Wasm and run in the browser.
I get the correct representation if I represent the bytes as Base64-encoded text and make an <a> element with an href to the string.
let base64_string = base64::encode(&file.bytes);
let download_url = format!("data:{};base64,{}",file.mime_type,base64_string);
// ... set href = &download_url inside dom
But this is horribly slow for files more than a couple KB and gets slower as more files get added.
What is the correct Rust -> JS conversion to use the create_object_url_with_blob() so that it works as expected?
It looks like the correct way to do this is to push your Uint8Array to a js_sys::Array first,
and because js_sys::Array implements JsCast you can use it directly inside the blob.
I assume this comes from some sort of internal representation of JavaScript types inside js_sys, and the behavior of the code in the question likely defaults to treating an array of bytes as text.
Working code:
let uint8arr = js_sys::Uint8Array::new(&unsafe { js_sys::Uint8Array::view(&bytes) }.into());
let array = js_sys::Array::new();
array.push(&uint8arr.buffer());
let blob = Blob::new_with_u8_array_sequence_and_options(
&array,
web_sys::BlobPropertyBag::new().type_("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
).unwrap();
let download_url = web_sys::Url::create_object_url_with_blob(&blob).unwrap();
This results in the bytes actually being used as the Word Document as opposed to written to an empty word doc.

Convert string to base64 byte array in swift and java give different value

Incase of android everything is working perfectly. I want to implement same feature in iOS too but getting different values. Please check the description with images below.
In Java/Android Case:
I tried to convert the string to base64 byte array in java like
byte[] data1 = Base64.decode(balance, Base64.DEFAULT);
Output:
In Swift3/iOS Case:
I tried to convert the string to base64 byte array in swift like
let data:Data = Data(base64Encoded: balance, options: NSData.Base64DecodingOptions(rawValue: 0))!
let data1:Array = (data.bytes)
Output:
Finally solved:
This is due to signed and unsigned integer, meaning unsigned vs signed (so 0 to 255 and -127 to 128). Here, we need to convert the UInt8 array to Int8 array and therefore the problem will be solved.
let intArray = data1.map { Int8(bitPattern: $0) }
In no case should you try to compare data on 2 systems the way you just did. That goes for all types but specially for raw data.
Raw data are NOT presentable without additional context which means any system that does present them may choose how to present them (raw data may represent some text in UTF8 or some ASCII, maybe jpeg image or png or raw RGB pixel data, it might be an audio sample or whatever). In your case one system is showing them as a list of signed 8bit integers while the other uses 8bit unsigned integers for the same thing. Another system might for instance show you a hex string which would look completely different.
As #Larme already mentioned these look the same as it is safe to assume that one system uses signed and the other unsigned values. So to convert from signed (Android) to unsigned (iOS) you need to convert negative values as unsigned = 256+signet so for instance -55 => 256 + (-55) = 201.
If you really need to compare data in your case it is the best to save them into some file as raw data. Then transfer that file to another system and compare native raw data to those in file to check there is really a difference.
EDIT (from comment):
Printing raw data as a string is a problem but there are a few ways. The thing is that many bytes are not printable as strings, may be whitespaces or some reserved codes but mostly the problem is that value of 0 means the end of string in most cases which may exist in the middle of your byte sequence.
So you already have 2 ways of printing byte by byte which is showing Int8 or Uint8 corresponding values. As described in comment converting directly to string may not work as easy as
let string = String(data: data, encoding: .utf8) // Will return nil for strange strings
One way of converting data to string may be to convert each byte into a corresponding character. Check this code:
let characterSequence = data.map { UnicodeScalar($0) } // Create an array of characters from bytes
let stringArray = characterSequence.map { String($0) } // Create an array of strings from array of characters
let myString = stringArray.reduce("", { $0 + $1 }) // Convert an array of strings to a single string
let myString2 = data.reduce("", { $0 + String(UnicodeScalar($1)) }) // Same thing in a single line
Then to test it I used:
let data = Data(bytes: Array(0...255)) // Generates with byte values of 0, 1, 2... up to 255
let myString2 = data.reduce("", { $0 + String(UnicodeScalar($1)) })
print(myString2)
The printing result is:
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
Then another popular way is using a hex string. It can be displayed as:
let hexString = data.reduce("", { $0 + String(format: "%02hhx",$1) })
print(hexString)
And with the same data as before the result is:
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
I hope this is enough but in general you could do pretty much anything with array of bytes and show them. For instance you could create an image treating bytes as RGB 8-bit per component if it would make sense. It might sound silly but if you are looking for some patterns it might be quite a witty solution.

How to read first and last 64kb of a video file in Swift

I want to use a subtitle API. It requires a md5 hash of first and last 64kb of the video file. I know how to do the md5 part just want to know how will I achieve to get the 128kb of data.
Here is the solution to the problem in Java which I am unable to implement in Swift. Stack
I have a video URL, How would I get the first and last 64kb from it? Get on AlamoFire then what?
below is how it's done in Java,
FileInputStream in = new FileInputStream("d:/1.avi");
byte[] a = new byte[64 * 1024];
in.read(a); //head
long p = in.getChannel().size() - 64 * 1024;
in.getChannel().position(p);
in.read(a); //tail
Here is how to do it correctly:
let data = try! Data(contentsOf: URL(string: <#Insert your URL#>)!) // should do some unwrapping precautions here
// first 64 bytes
let first = data.subdata(in: 0 ..< 65336) // 65336 bytes = 1kb (if 1kb = 1024 bytes)
// last 64 bytes
let last = data.subdata(in: (data.count - 65336)..<data.count) // data.count - 65366 = last 64 bytes of the file
So first you download the file (eg with Alamofire). Once that is done, place the URL into the string: parameter of the URL initialiser.
Then, use the variables first and last to get the md5.

Converting C pointers to Swift 3

I have the code:
let data = Data(bytes: UnsafePointer<UInt8>(audioBuffer.mData), count: Int(bufferSize))
and
let u16 = UnsafePointer<Int32>(audioBuffer.mData).pointee
Both of which work in Swift 2.3 but not in Swift 3. How do I convert them so they act equivalently? (and why?)
To read 16-bit audio samples from Audio Unit callback buffers in Swift 3, I use:
let bufferPointer = UnsafeMutableRawPointer(mBuffers.mData)
if var bptr = bufferPointer {
for i in 0..<(Int(frameCount)) {
let oneSampleI16 = bptr.assumingMemoryBound(to: Int16.self).pointee
// do something with the audio sample
bptr += 1
}
}
The rest of the Audio Session and Audio Unit code is in this gist: https://gist.github.com/hotpaw2/630a466cc830e3d129b9
I can't say I understand this well, nor have I read the document, but it looks like swift3 pointer casts are scoped to avoid or limit aliasing, so you can't (easily) have two different views of the same piece of memory, or at least not for very long. This means you must either copy the cast data out or do whatever you need to do within a cast callback.
Why eliminate aliasing? I guess it makes for happier compilers.
For Data:
// [NS]Data. probably copying the data
Data(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
For numeric arrays:
// cast the data to Int32s & (optionally) copy the data out
let umpInt32 = audioBuffer.mData!.assumingMemoryBound(to: Int32.self)
let frameCount = Int(audioBuffer.mDataByteSize/4)
var u32 = [Int32](repeating: 0, count: frameCount)
// copy data from buffer
u32.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: umpInt32, count: frameCount)
}
p.s. there's some confusion in your code. is u16 supposed to be an array of Int32s? Or UInt16s? Or something else?
Check the latest reference of Data.init(bytes:count:).
The type of the parameter bytes is UnsafeRawPointer, which accepts UnsafeMutableRawPointer. And the type of AudioBuffer.mData is UnsafeMutableRawPointer?. You have no need to convert using initializer.
let data = Data(bytes: audioBuffer.mData!, count: Int(bufferSize))
(You just need to explicitly unwrap mData, as it is imported as nullable type, UnsafeMutableRawPointer?, but you need to pass non-nil UnsafeRawPointer (or UnsafeMutableRawPointer).
The second example, you'd better check what sort of methods are available for UnsafeMutableRawPointer. You can find load(fromByteOffset:as:) method, and can use it like this.
let i32 = audioBuffer.mData!.load(as: Int32.self)
`load(

Converting a hexadecimal string to a decimal integer

I'm writing a Rust program that reads off of an I2C bus and saves the data. When I read the I2C bus, I get hex values like 0x11, 0x22, etc.
Right now, I can only handle this as a string and save it as is. Is there a way I can parse this into an integer? Is there any built in function for it?
In most cases, you want to parse more than one hex byte at once. In those cases, use the hex crate.
parse this into an integer
You want to use from_str_radix. It's implemented on the integer types.
use std::i64;
fn main() {
let z = i64::from_str_radix("1f", 16);
println!("{:?}", z);
}
If your strings actually have the 0x prefix, then you will need to skip over them. The best way to do that is via trim_start_matches or strip_prefix:
use std::i64;
fn main() {
let raw = "0x1f";
let without_prefix = raw.trim_start_matches("0x");
let z = i64::from_str_radix(without_prefix, 16);
println!("{:?}", z);
}

Resources