Почему универсальная функция, реплицирующая хэш Си для целых без знака, всегда возвращает ноль? - PullRequest
0 голосов
/ 23 мая 2018

Я пытаюсь прочитать двоичные 16-битные машинные инструкции из 16-битной архитектуры (точная природа здесь не имеет значения) и вывести их обратно в виде шестнадцатеричных значений.В C я нашел это простым, используя функцию fread для считывания 16 битов в uint16_t.

. Я решил, что попытаюсь повторить fread в Rust.Кажется, это довольно тривиально, если я могу заранее знать точный размер переменной, в которую выполняется чтение, и у меня это работало специально для 16 битов.

Я решил, что хочу попробоватьсделать функцию fread обобщенной для различных встроенных целочисленных типов без знака.Для этого я придумал следующую функцию, используя некоторые черты из ящика Num:

fn fread<T>(
    buffer: &mut T,
    element_count: usize,
    stream: &mut BufReader<File>,
) -> Result<usize, std::io::Error>
where
    T: num::PrimInt + num::Unsigned,
{
    let type_size = std::mem::size_of::<T>();
    let mut buf = Vec::with_capacity(element_count * type_size);
    let buf_slice = buf.as_mut_slice();

    let bytes_read = match stream.read_exact(buf_slice) {
        Ok(()) => element_count * type_size,
        Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => 0,
        Err(e) => panic!("{}", e),
    };

    *buffer = buf_slice
        .iter()
        .enumerate()
        .map(|(i, &b)| {
            let mut holder2: T = num::zero();
            holder2 = holder2 | T::from(b).expect("Casting from u8 to T failed");
            holder2 << ((type_size - i) * 8)
        })
        .fold(num::zero(), |acc, h| acc | h);
    Ok(bytes_read)
}

Проблема в том, что когда я вызываю ее в основной функции, мне кажется, что я всегда получаю 0x00 обратно, но число прочитанных байтов, возвращаемых функцией, всегда равно 2, так что программа входит в бесконечный цикл:

extern crate num;

use std::fs::File;
use std::io::BufReader;
use std::io::prelude::Read;

fn main() -> Result<(), std::io::Error> {
    let cmd_line_args = std::env::args().collect::<Vec<_>>();

    let f = File::open(&cmd_line_args[1])?;
    let mut reader = BufReader::new(f);
    let mut instructions: Vec<u16> = Vec::new();

    let mut next_instruction: u16 = 0;
    fread(&mut next_instruction, 1, &mut reader)?;

    let base_address = next_instruction;

    while fread(&mut next_instruction, 1, &mut reader)? > 0 {
        instructions.push(next_instruction);
    }

    println!("{:#04x}", base_address);

    for i in instructions {
        println!("0x{:04x}", i);
    }

    Ok(())
}

Мне кажется, что я почему-то ничего не читаю из файла, так что функция всегда просто возвращает количество байтов, которое она должна была прочитать.Я явно не правильно что-то здесь использую, но честно говоря не уверен, что делаю неправильно.

Это скомпилировано в Rust 1.26 stable для Windows, если это имеет значение.

Что я делаю не так, и что я должен делать по-другому для репликации fread?Я понимаю, что это, вероятно, случай проблемы XY (в которой почти наверняка есть лучший способ Rust многократно читать некоторые байты из файла и упаковывать их в одно целое число без знака), но мне действительно любопытно, что яЯ делаю неправильно здесь.

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

в том смысле, что почти наверняка есть лучший способ Rust многократно читать некоторые байты из файла и упаковывать их в одно целое число без знака

Да, использовать ящик для метеорологических данных .Это не требует ненужного выделения кучи (Vec в исходном коде):

extern crate byteorder;

use byteorder::{LittleEndian, ReadBytesExt};
use std::{
    fs::File, io::{self, BufReader, Read},
};

fn read_instructions_to_end<R>(mut rdr: R) -> io::Result<Vec<u16>>
where
    R: Read,
{
    let mut instructions = Vec::new();
    loop {
        match rdr.read_u16::<LittleEndian>() {
            Ok(instruction) => instructions.push(instruction),
            Err(e) => {
                return if e.kind() == std::io::ErrorKind::UnexpectedEof {
                    Ok(instructions)
                } else {
                    Err(e)
                }
            }
        }
    }
}

fn main() -> Result<(), std::io::Error> {
    let name = std::env::args().skip(1).next().expect("no file name");

    let f = File::open(name)?;
    let mut f = BufReader::new(f);

    let base_address = f.read_u16::<LittleEndian>()?;
    let instructions = read_instructions_to_end(f)?;

    println!("{:#04x}", base_address);

    for i in &instructions {
        println!("0x{:04x}", i);
    }

    Ok(())
}
0 голосов
/ 23 мая 2018

Ваша проблема в том, что эта строка:

let mut buf = Vec::with_capacity(element_count * type_size);

создает вектор нулевой длины , даже если он выделяет память для element_count * type_size байтов.Поэтому вы просите stream.read_exact прочитать ноль байтов.Один из способов исправить это - заменить приведенную выше строку на:

let mut buf = vec![0; element_count * type_size];

. Примечание: после успешного чтения bytes_read получает количество байтов, которое вы ожидали прочитать, а не количество байтов, которое вы на самом делечитать.Вам, вероятно, следует использовать std::mem::size_of_val (buf_slice), чтобы получить истинное количество байтов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...