I finished reading The Rust Programming Languange some time ago and I'm creating a simple http-server for the sake of teaching myself Rust.
In a function meant to parse a &[u8] and create a HttpRequest object, I noticed that a loop that should be parsing the headers is actually not updating some variables used to track the state of parsing:
pub fn parse_request(buffer: &[u8]) -> Result<HttpRequest, &'static str> {
let (mut line, mut curr_req_reader_pos) = next_req_line(buffer, 0);
let (method, path, version) = parse_method_path_and_version(&line)?;
let (line, curr_req_reader_pos) = next_req_line(buffer, curr_req_reader_pos);
//eprintln!("--- got next line: {}, {} ---", curr_req_reader_pos, String::from_utf8_lossy(line));
let mut headers = vec![];
let mut lel = 0;
while line.len() > 0 {
//eprintln!("LOOP");
let header = parse_header(&line);
eprintln!("--- parsed header: {} ---", String::from_utf8_lossy(line));
headers.push(header);
let (line, curr_req_reader_pos) = next_req_line(buffer, curr_req_reader_pos);
//eprintln!("--- got next line: {}, {} ---", curr_req_reader_pos, String::from_utf8_lossy(line));
let (line, curr_req_reader_pos) = next_req_line(buffer, curr_req_reader_pos);
//eprintln!("--- got next line: {}, {} ---", curr_req_reader_pos, String::from_utf8_lossy(line));
//eprintln!("LOOP");
lel += 10;
//break;
}
let (line, curr_req_reader_pos) = next_req_line(buffer, curr_req_reader_pos);
//eprintln!("--- got next line: {}, {} ---", curr_req_reader_pos, String::from_utf8_lossy(line));
//eprintln!("{}", lel);
let has_body;
match method.as_ref() {
"POST" | "PUT" | "PATCH" => has_body = true,
_ => has_body = true
};
let body;
if has_body {
let (line, curr_req_reader_pos) = next_req_line(buffer, curr_req_reader_pos);
body = String::from_utf8_lossy(line).to_string();
} else {
body = String::new();
}
Ok(HttpRequest {
method,
path,
version,
headers,
has_body,
body
})
}
There's also tests for each method:
#[test]
fn test_next_req_line() {
let req = "GET / HTTP/1.1\r\nContent-Length: 3\r\n\r\n\r\n\r\nlel".as_bytes();
let (mut lel, mut pos) = next_req_line(req, 0);
assert_eq!("GET / HTTP/1.1", String::from_utf8_lossy(lel));
assert!(lel.len() > 0);
assert_eq!(16, pos);
let (lel, pos) = next_req_line(req, pos);
assert_eq!("Content-Length: 3", String::from_utf8_lossy(lel));
assert!(lel.len() > 0);
assert_eq!(35, pos);
let (lel, pos) = next_req_line(req, pos);
assert_eq!("", String::from_utf8_lossy(lel));
assert!(lel.len() == 0);
assert_eq!(37, pos);
let (lel, pos) = next_req_line(req, pos);
assert_eq!("", String::from_utf8_lossy(lel));
assert!(lel.len() == 0);
let (lel, pos) = next_req_line(req, pos);
assert_eq!("", String::from_utf8_lossy(lel));
assert!(lel.len() == 0);
let (lel, pos) = next_req_line(req, pos);
assert_eq!("lel", String::from_utf8_lossy(lel));
assert!(lel.len() == 3);
}
#[test]
fn test_parse_request() {
let mut request = String::from("GET / HTTP/1.1\r\n");
request.push_str("Content-Length: 3\r\n");
request.push_str("\r\n");
request.push_str("lel");
let request = request.as_bytes();
match parse_request(&request) {
Ok(http_request) => {
assert_eq!(http_request.method, "GET");
assert_eq!(http_request.path, "/");
assert_eq!(http_request.version, "HTTP/1.1");
},
Err(_) => {
println!("Failed");
}
}
}
The commented lines are just to show what's happening, and that's where I saw this output:
Finished test [unoptimized + debuginfo] target(s) in 0.53s
Running target/debug/deps/rweb-6bbc2a3130f7e3d9
running 4 tests
test http::testing::test_next_req_line ... ok
test http::testing::test_parse_header ... ok
test http::testing::test_parse_method_path_and_version ... ok
--- parsed method path version ---
--- got next line: 35, Content-Length: 3 ---
LOOP
--- parsed header: Content-Length: 3 ---
--- got next line: 37, ---
--- got next line: 39, lel ---
LOOP
--- got next line: 37, ---
10
test http::testing::test_parse_request ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
It seems that the while loop is not re-assigning the line and curr_req_reader_pos variables, but for me it made perfect sense to expect that the loop would update these variables and parse every header. However, it works perfecly outside a loop, as anyone can see in the tests.
I can't figure out why this happens with my current undertanding of Rust, why does it happen?
You aren't "reassigning" variables in the loop, you create new variables that temporarily shadow the outer ones and then go out of scope when the iteration ends.
Here's an illustration as to what is going on in simplified pseudocode:
let x0 = 0
{
let x1 = x0 + 1 // original x gets shadowed, x = 1
let x2 = x1 + 1 // x1 gets shadowed, x = 2
} // both x1 and x2 go out of scope
let x1 = x0 + 1 // x = 1
Related
This my script for minting NFT:
{-# INLINABLE mkPolicy #-}
mkPolicy :: BuiltinData -> PlutusV2.ScriptContext -> Bool
mkPolicy _ ctx = traceIfFalse "wrong amount minted" checkNFTAmount
where
info :: PlutusV2.TxInfo
info = PlutusV2.scriptContextTxInfo ctx
-- hasUTxO :: Bool
-- hasUTxO = any (\i -> PlutusV2.txInInfoOutRef i == mpTxOutRef r) $ PlutusV2.txInfoInputs info
checkNFTAmount :: Bool
checkNFTAmount = case Value.flattenValue (PlutusV2.txInfoMint info) of
[(cs, tn', amt)] -> cs == ownCurrencySymbol ctx && tn' == PlutusV2.TokenName "" && amt == 1
_ -> False
{-
As a Minting Policy
-}
compiledCode :: PlutusTx.CompiledCode (BuiltinData -> BuiltinData -> ())
compiledCode = $$(PlutusTx.compile [|| wrap ||])
where
wrap = Scripts.mkUntypedMintingPolicy mkPolicy
policy :: Scripts.MintingPolicy
policy = PlutusV2.mkMintingPolicyScript compiledCode
script :: PlutusV2.Script
script = PlutusV2.unMintingPolicyScript policy
{-
As a Short Byte String
-}
scriptSBS :: SBS.ShortByteString
scriptSBS = SBS.toShort . LBS.toStrict $ serialise script
{-
As a Serialised Script
-}
serialisedScript :: PlutusScript PlutusScriptV2
serialisedScript = PlutusScriptSerialised scriptSBS
writeSerialisedScript :: IO ()
writeSerialisedScript = void $ writeFileTextEnvelope "nft-mint-V2.plutus" Nothing serialisedScript
I'm using Mesh to minting NFT with that script
const walletAddr = wallet.getPaymentAddress();
const addressUtxo: UTxO[] = await provider.fetchAddressUTxOs(walletAddr);
const redeemer: Partial<Action> = {
tag: "MINT",
data: {
alternative: 0,
fields: [],
},
};
const assetMetadata: AssetMetadata = {
name: "MyNFT",
image: "https://picsum.photos/200",
mediaType: "image/jpg",
description: "This NFT is minted by me.",
};
const asset: Mint = {
assetName: "MyNFT",
assetQuantity: "1",
metadata: assetMetadata,
label: "721",
recipient: walletAddr,
};
// Mint NFT
const tx = new Transaction({ initiator: wallet });
tx.mintAsset(script, asset, redeemer);
tx.setCollateral([addressUtxo[0]]);
const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx, true);
try {
const txHash = await wallet.submitTx(signedTx);
console.log(txHash);
} catch (e) {
console.log(e);
}
Unfortunately, it returned with this error:
transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage
(ApplyTxError
[UtxowFailure
(UtxoFailure
(FromAlonzoUtxoFail
(UtxosFailure
(ValidationTagMismatch
(IsValid True)
(FailedUnexpectedly
(PlutusFailure \\\"\\\\nThe 2 arg plutus script (PlutusScript PlutusV2 ScriptHash \\\\\\\"77f807bc9403ef0177cc2a9956bfd5628ee649680041ccf48a198fc0\\\\\\\") fails.
\\\\nCekError An error has occurred:
User error:\\\\nThe machine terminated because of an error, either from a built-in function or from an explicit use of 'error'.
\\\\nThe protocol version is: ProtVer {pvMajor = 7, pvMinor = 0}\\\\nThe redeemer is: Constr 0 []\\\\
nThe second data argument, does not decode to a context
Does anyone faced this error before? As the error said "The second data argument, does not decode to a context" and I specify context in my script like this, I think the problem is with the context param. Or what is wrong with my script.
I am trying to scan ECC Data Matrix code with binary content, but if there is a NULL byte I can only get the string up to there.
Unfortunately, I have no control over these matrix codes, as I have to scan the codes provided.
Does somebody has any idea?
Is it possibly to convert the rawData?
It would be enough if I received the content as a hex value.
The rawData is allready hex, but not as expected, maybe it is also corrupt or in an unknown coding.
Does somebody know encoding of rawdata?
see https://developers.google.com/ml-kit/reference/ios/mlkitbarcodescanning/api/reference/Classes/MLKBarcode#rawdata
I found a solution for me:
Here my Code for React-Native:
import {DataMatrixDecodedBitStreamParser, ZXingStringEncoding} from "#zxing/library";
const bin2hex = (s)=> {
// discuss at: https://locutus.io/php/bin2hex/
// original by: Kevin van Zonneveld (https://kvz.io)
// bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
// bugfixed by: Linuxworld
// improved by: ntoniazzi (https://locutus.io/php/bin2hex:361#comment_177616)
// example 1: bin2hex('Kev')
// returns 1: '4b6576'
// example 2: bin2hex(String.fromCharCode(0x00))
// returns 2: '00'
let i;
let l;
let o = '';
let n;
s += '';
for (i = 0, l = s.length; i < l; i++) {
n = s.charCodeAt(i)
.toString(16);
o += n.length < 2 ? '0' + n : n;
}
return o;
}
const hex2bin = (s)=> {
// discuss at: https://locutus.io/php/hex2bin/
// original by: Dumitru Uzun (https://duzun.me)
// example 1: hex2bin('44696d61')
// returns 1: 'Dima'
// example 2: hex2bin('00')
// returns 2: '\x00'
// example 3: hex2bin('2f1q')
// returns 3: false
const ret = []
let i = 0
let l
s += ''
for (l = s.length; i < l; i += 2) {
const c = parseInt(s.substr(i, 1), 16);
const k = parseInt(s.substr(i + 1, 1), 16);
if (isNaN(c) || isNaN(k)) return false;
ret.push((c << 4) | k);
}
return String.fromCharCode.apply(String, ret);
}
const fromHexString = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
const matrixcodeRAW2HEX = raw_hex => {
let data = fromHexString(raw_hex);
try {
global.Buffer = global.Buffer || require('buffer').Buffer;
ZXingStringEncoding.customDecoder = (stringContent, encodingName) => {
let encodingName2 = encodingName;
if(encodingName.toLowerCase()=="iso-8859-1"){
encodingName2="latin1";
}
return new Buffer(stringContent).toString(encodingName2);
}
ZXingStringEncoding.customEncoder = (stringContent, encodingName) => {
let encodingName2 = encodingName;
if(encodingName.toLowerCase()=="iso-8859-1"){
encodingName2="latin1";
}
return new Buffer(stringContent).toString(encodingName2);
};
let newData = DataMatrixDecodedBitStreamParser.decode(data);
return bin2hex(newData.getText());
}catch (e) {
console.log(e);
}
}
My function will return the original data as hex, so there is no problem with NUL, but you can also use hex2bin to get it as a Text if necessary.
I´m using the zxing polyfill for JS => https://github.com/zxing-js/library, cause JS does not Cut String like Objective C do.
I found out in Objective C NUL always will cut a string, so there is no solution yet.
I've been trying to use the CommonCrypto in iOS to do a blockwise decryption of a larger Data object, but I can't get it to work. I've been reading the documentation, and looked at several examples from Objective-C - none of which I managed to get to work in an Objective-C environment. After 3 days of coding this I'm starting to wear out and I need some help.
I can decrypt and encrypt fine using the CCCrypto approach, but since I use rather large files this eats up too much memory for iOS to allow me to use this method. Thus I need to do this more efficiently and I decided to try an approach where I decipher one block at a time and then replace the block I deciphered with the resulting data block. The code runs, it seems to be working except for the fact that the data I get won't decode back to an UTF8 String.
Code for using CCCryptoCreate() with symmetric block deciphering (DataExtension)
mutating func decryptUsingCCCryptoCreate() {
// Get the first 16 bytes as IV
let IV = self.prefix(kCCBlockSizeAES128)
// Get key as array of bytes
let key = "ABCDEFGHIJKLMNOPQRSABCDEFGHIJKLM".data(using: .utf8) ?? Data()
// Setup totalSize
let totalSize = self.count
let operation = kCCDecrypt
let algorithm = kCCAlgorithmAES
let options = kCCOptionPKCS7Padding
var cryptorRef: CCCryptorRef?
// Step one is to create the CCCryptor with correct parameters for AES128 decryption
var status = CCCryptorCreate(CCOperation(operation), CCAlgorithm(algorithm), CCOptions(options), key.withUnsafeBytes { $0.baseAddress }, key.count, IV.withUnsafeBytes { $0.baseAddress }, &cryptorRef)
if status != kCCSuccess {
print("Failed on create: \(status.description)")
return
}
var dataOutMoved: size_t = 0 // The actual data moved
var dataInLength: size_t = kCCBlockSizeAES128 // The in size will always be the size of a kCCBlockSizeAES128
var dataOutLength: size_t = CCCryptorGetOutputLength(cryptorRef, dataInLength, false) // DataOutLength is always less than or equal to the dataInLength
var totalLength: size_t = 0 // The actual length of the deciphered data
var filePtr: size_t = 0 // Keeps track of the current position in the deciphering process
var startByte: Int = 0 // Increments each time with the kCCBlockSizeAES128 until we are done
var dataIn = Data() // Buffer to store the encrypted block to be deciphered
var dataOut = Data() // Buffer to store the decrypted block result
// While startByte is less than totalSize we continue to decrypt the next block
while startByte <= totalSize {
if startByte + kCCBlockSizeAES128 > totalSize {
dataInLength = totalSize - startByte
} else {
dataInLength = kCCBlockSizeAES128
}
// Next block to decrypt
guard let rangeToDecrypt = Range(NSRange(location: startByte, length: dataInLength)) else { return }
dataIn = self.subdata(in: rangeToDecrypt)
// Decipher the block
status = CCCryptorUpdate(cryptorRef, dataIn.withUnsafeBytes { $0.baseAddress }, dataInLength, dataOut.withUnsafeMutableBytes { $0.baseAddress }, dataOutLength, &dataOutMoved)
if status != kCCSuccess {
print("Failed on Update: \(status.description)")
return
}
// Replace the encrypted block with the decrypted block
let rangeToReplace = Range(NSRange(location: filePtr, length: dataOutMoved))!
self.replaceSubrange(rangeToReplace, with: dataOut.withUnsafeBytes { $0.baseAddress! }, count: dataOutMoved)
totalLength += dataOutMoved
filePtr += dataOutMoved
startByte += kCCBlockSizeAES128
}
// Finalize the deciphering
status = CCCryptorFinal(cryptorRef, dataOut.withUnsafeMutableBytes { $0.baseAddress }, dataOutLength, &dataOutMoved)
totalLength += dataOutMoved
if status != kCCSuccess {
print("Failed on final: \(status.description)")
return
}
// We replace the final deciphered block
let decryptedRange = Range(NSRange(location: filePtr, length: dataOutMoved))!
self.replaceSubrange(decryptedRange, with: dataOut.withUnsafeBytes { $0.baseAddress! }, count: dataOut.count)
// Since we are using padding the CCCryptorFinal can contain padding which needs to be truncated.
self = self.prefix(totalLength)
// Finish the CCCryptor process
CCCryptorRelease(cryptorRef)
}
Code for the encryption using CCCrypto()
mutating func encryptUsingCCCrypto() {
let sa = String(data: self, encoding: .utf8) ?? ""
print("Before encryption: \(sa)")
let now = Date()
let key = "ABCDEFGHIJKLMNOPQRSABCDEFGHIJKLM".data(using: .utf8) ?? Data()
let ivRandomData = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
let blockSize = kCCBlockSizeAES128
let bufferSize = self.count + blockSize
var encryptedSize = 0
let cryptStatus = CCCrypt(UInt32(kCCEncrypt),
UInt32(kCCAlgorithmAES),
UInt32(kCCOptionPKCS7Padding),
key.withUnsafeBytes { $0.baseAddress },
key.count,
ivRandomData.withUnsafeBytes { $0.baseAddress },
self.withUnsafeBytes { $0.baseAddress },
self.count,
self.withUnsafeMutableBytes { $0.baseAddress },
bufferSize,
&encryptedSize)
self = self.prefix(encryptedSize)
let s = String(data: self, encoding: .utf8) ?? ""
print("Result: \(s)")
}
I use the code like this:
let string = "1234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234"
var data = string.data(using: .utf8) ?? Data()
data.encryptUsingCCCrypto()
data.decryptUsingCCCryptoCreate() // I get a Data object with 112 bytes here
let s = String(data: data, encoding: .utf8) ?? "" // String is nil, and is provided ""
I'm trying to implement a simple REPL calculator in Rust and I'm hitting brick walls all over the place.
I'm consuming chars while iterating over a hardcoded string. When I hit a numeric character I want to pass control over to a function that will consume the rest of the number (assuming the number has more than one digit) and return the number, converted to an Integer.
I'm having trouble with passing Chars iterator to a function. The error I'm getting is use of moved value: 'iter'.
I understand that I can't mutate something that I gave to someone else - something that had its ownership moved - but I don't know any other way of doing this, especially since the Chars iterator is non-copyable.
#[derive(Clone, Debug)]
enum Token {
Addition,
Substraction,
Multiplication,
Division,
Integer(i32),
Error,
}
fn consume_number(mut iter: std::str::Chars) -> Option<i32> {
while let Some(item) = iter.next() {
println!("{:?}", item);
}
return Some(1337);
}
fn tokenize(line: &str) -> Vec<Token> {
let mut iter = line.chars();
let mut tokens = Vec::new();
let mut token;
while let Some(c) = iter.next() {
if c.is_whitespace() { continue };
if c.is_digit(10) {
token = match consume_number(iter) {
Some(i32) => Token::Integer(i32),
None => Token::Error,
};
} else {
token = match c {
'+' => Token::Addition,
'-' => Token::Substraction,
'*' => Token::Multiplication,
'/' => Token::Division,
_ => Token::Error,
};
};
tokens.push(token);
}
return tokens;
}
fn main() {
let line = "631 * 32 + 212 - 15 / 89";
println!("{:?}", tokenize(&line));
}
The answer is yes, it's done in the FromIterator trait.
What you experience here is much more basic:
fn consume_number(mut iter: std::str::Chars) -> Option<i32> { ... }
while let Some(c) = iter.next() {
...
match_consume_number(iter)
...
}
When calling match_consume_number you are transferring ownership of the iterator to it. It means that at the next iteration of the loop body, this iter variable is no longer available.
If the iterator is meant to still be usable afterward, you should pass a reference to it:
fn consume_number(iter: &mut std::str::Chars) -> Option<i32> { ... }
while let Some(c) = iter.next() {
...
match_consume_number(&mut iter)
...
}
You were close!
I'd like to write a prompt function that sends a passed-in string to stdout and then returns the string that it reads from stdin. How could I test it?
Here is an example of the function:
fn prompt(question: String) -> String {
let mut stdin = BufferedReader::new(stdin());
print!("{}", question);
match stdin.read_line() {
Ok(line) => line,
Err(e) => panic!(e),
}
}
And here is my testing attempt
#[test]
fn try_to_test_stdout() {
let writer: Vec<u8> = vec![];
set_stdout(Box::new(writer));
print!("testing");
// `writer` is now gone, can't check to see if "testing" was sent
}
Use dependency injection. Coupling it with generics and monomorphism, you don't lose any performance:
use std::io::{self, BufRead, Write};
fn prompt<R, W>(mut reader: R, mut writer: W, question: &str) -> String
where
R: BufRead,
W: Write,
{
write!(&mut writer, "{}", question).expect("Unable to write");
let mut s = String::new();
reader.read_line(&mut s).expect("Unable to read");
s
}
#[test]
fn test_with_in_memory() {
let input = b"I'm George";
let mut output = Vec::new();
let answer = prompt(&input[..], &mut output, "Who goes there?");
let output = String::from_utf8(output).expect("Not UTF-8");
assert_eq!("Who goes there?", output);
assert_eq!("I'm George", answer);
}
fn main() {
let stdio = io::stdin();
let input = stdio.lock();
let output = io::stdout();
let answer = prompt(input, output, "Who goes there?");
println!("was: {}", answer);
}
In many cases, you'd want to actually propagate the error back up to the caller instead of using expect, as IO is a very common place for failures to occur.
This can be extended beyond functions into methods:
use std::io::{self, BufRead, Write};
struct Quizzer<R, W> {
reader: R,
writer: W,
}
impl<R, W> Quizzer<R, W>
where
R: BufRead,
W: Write,
{
fn prompt(&mut self, question: &str) -> String {
write!(&mut self.writer, "{}", question).expect("Unable to write");
let mut s = String::new();
self.reader.read_line(&mut s).expect("Unable to read");
s
}
}
#[test]
fn test_with_in_memory() {
let input = b"I'm George";
let mut output = Vec::new();
let answer = {
let mut quizzer = Quizzer {
reader: &input[..],
writer: &mut output,
};
quizzer.prompt("Who goes there?")
};
let output = String::from_utf8(output).expect("Not UTF-8");
assert_eq!("Who goes there?", output);
assert_eq!("I'm George", answer);
}
fn main() {
let stdio = io::stdin();
let input = stdio.lock();
let output = io::stdout();
let mut quizzer = Quizzer {
reader: input,
writer: output,
};
let answer = quizzer.prompt("Who goes there?");
println!("was: {}", answer);
}