<commit_before>use crate::llvm;
use crate::llvm::{False, ObjectFile, mk_section_iter};
use crate::llvm::archive_ro::ArchiveRO;
use rustc::middle::cstore::MetadataLoader;
use rustc_target::spec::Target;

use rustc_data_structures::owning_ref::OwningRef;
use rustc_codegen_ssa::METADATA_FILENAME;

use std::path::Path;
use std::slice;
use rustc_fs_util::path_to_c_string;

pub use rustc_data_structures::sync::MetadataRef;

pub struct LlvmMetadataLoader;

impl MetadataLoader for LlvmMetadataLoader {
    fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
        // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap
        // internally to read the file. We also avoid even using a memcpy by
        // just keeping the archive along while the metadata is in use.
        let archive = ArchiveRO::open(filename)
            .map(|ar| OwningRef::new(box ar))
            .map_err(|e| {
                debug!("llvm didn't like `{}`: {}", filename.display(), e);
                format!("failed to read rlib metadata in '{}': {}", filename.display(), e)
            })?;
        let buf: OwningRef<_, [u8]> = archive
            .try_map(|ar| {
                ar.iter()
                    .filter_map(|s| s.ok())
                    .find(|sect| sect.name() == Some(METADATA_FILENAME))
                    .map(|s| s.data())
                    .ok_or_else(|| {
                        debug!("didn't find '{}' in the archive", METADATA_FILENAME);
                        format!("failed to read rlib metadata: '{}'",
                                filename.display())
                    })
            })?;
        Ok(rustc_erase_owner!(buf))
    }

    fn get_dylib_metadata(&self,
                          target: &Target,
                          filename: &Path)
                          -> Result<MetadataRef, String> {
        unsafe {
            let buf = path_to_c_string(filename);
            let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr())
                .ok_or_else(|| format!("error reading library: '{}'", filename.display()))?;
            let of = ObjectFile::new(mb)
                .map(|of| OwningRef::new(box of))
                .ok_or_else(|| format!("provided path not an object file: '{}'",
                                       filename.display()))?;
            let buf = of.try_map(|of| search_meta_section(of, target, filename))?;
            Ok(rustc_erase_owner!(buf))
        }
    }
}

fn search_meta_section<'a>(of: &'a ObjectFile,
                           target: &Target,
                           filename: &Path)
                           -> Result<&'a [u8], String> {
    unsafe {
        let si = mk_section_iter(of.llof);
        while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
            let mut name_buf = None;
            let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf);
            let name = name_buf.map_or(
                "".to_string(),
                |buf| String::from_utf8(
                    slice::from_raw_parts(buf.as_ptr() as *const u8,
                                          name_len as usize)
                    .to_vec()).unwrap());
            debug!("get_metadata_section: name {}", name);
            if read_metadata_section_name(target) == name {
                let cbuf = llvm::LLVMGetSectionContents(si.llsi);
                let csz = llvm::LLVMGetSectionSize(si.llsi) as usize;
                // The buffer is valid while the object file is around
                let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz);
                return Ok(buf);
            }
            llvm::LLVMMoveToNextSection(si.llsi);
        }
    }
    Err(format!("metadata not found: '{}'", filename.display()))
}

pub fn metadata_section_name(target: &Target) -> &'static str {
    // Historical note:
    //
    // When using link.exe it was seen that the section name `.note.rustc`
    // was getting shortened to `.note.ru`, and according to the PE and COFF
    // specification:
    //
    // > Executable images do not use a string table and do not support
    // > section names longer than 8 characters
    //
    // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx
    //
    // As a result, we choose a slightly shorter name! As to why<commit_msg>format a bit<commit_after>use crate::llvm;
use crate::llvm::{False, ObjectFile, mk_section_iter};
use crate::llvm::archive_ro::ArchiveRO;
use rustc::middle::cstore::MetadataLoader;
use rustc_target::spec::Target;

use rustc_data_structures::owning_ref::OwningRef;
use rustc_codegen_ssa::METADATA_FILENAME;

use std::path::Path;
use std::slice;
use rustc_fs_util::path_to_c_string;

pub use rustc_data_structures::sync::MetadataRef;

pub struct LlvmMetadataLoader;

impl MetadataLoader for LlvmMetadataLoader {
    fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
        // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap
        // internally to read the file. We also avoid even using a memcpy by
        // just keeping the archive along while the metadata is in use.
        let archive = ArchiveRO::open(filename)
            .map(|ar| OwningRef::new(box ar))
            .map_err(|e| {
                debug!("llvm didn't like `{}`: {}", filename.display(), e);
                format!("failed to read rlib metadata in '{}': {}", filename.display(), e)
            })?;
        let buf: OwningRef<_, [u8]> = archive
            .try_map(|ar| {
                ar.iter()
                    .filter_map(|s| s.ok())
                    .find(|sect| sect.name() == Some(METADATA_FILENAME))
                    .map(|s| s.data())
                    .ok_or_else(|| {
                        debug!("didn't find '{}' in the archive", METADATA_FILENAME);
                        format!("failed to read rlib metadata: '{}'",
                                filename.display())
                    })
            })?;
        Ok(rustc_erase_owner!(buf))
    }

    fn get_dylib_metadata(&self,
                          target: &Target,
                          filename: &Path)
                          -> Result<MetadataRef, String> {
        unsafe {
            let buf = path_to_c_string(filename);
            let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr())
                .ok_or_else(|| format!("error reading library: '{}'", filename.display()))?;
            let of = ObjectFile::new(mb)
                .map(|of| OwningRef::new(box of))
                .ok_or_else(|| format!("provided path not an object file: '{}'",
                                       filename.display()))?;
            let buf = of.try_map(|of| search_meta_section(of, target, filename))?;
            Ok(rustc_erase_owner!(buf))
        }
    }
}

fn search_meta_section<'a>(of: &'a ObjectFile,
                           target: &Target,
                           filename: &Path)
                           -> Result<&'a [u8], String> {
    unsafe {
        let si = mk_section_iter(of.llof);
        while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
            let mut name_buf = None;
            let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf);
            let name = name_buf.map_or(
                String::new(), // We got a NULL ptr, ignore `name_len`.
                |buf| String::from_utf8(
                    slice::from_raw_parts(buf.as_ptr() as *const u8,
                                          name_len as usize)
                    .to_vec()
                ).unwrap()
            );
            debug!("get_metadata_section: name {}", name);
            if read_metadata_section_name(target) == name {
                let cbuf = llvm::LLVMGetSectionContents(si.llsi);
                let csz = llvm::LLVMGetSectionSize(si.llsi) as usize;
                // The buffer is valid while the object file is around
                let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz);
                return Ok(buf);
            }
            llvm::LLVMMoveToNextSection(si.llsi);
        }
    }
    Err(format!("metadata not found: '{}'", filename.display()))
}

pub fn metadata_section_name(target: &Target) -> &'static str {
    // Historical note:
    //
    // When using link.exe it was seen that the section name `.note.rustc`
    // was getting shortened to `.note.ru`, and according to the PE and COFF
    // specification:
    //
    // > Executable images do not use a string table and do not support
    // > section names longer than 8 characters
    //
    // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx
    //
    // As a result, we choose a slightly shorter name! As to why
{'fraction_non_alphanumeric': 0.12518376116702476, 'fraction_numerical': 0.0029401786723962458, 'mean_word_length': 1.8835996087381806, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 25, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2122642', 'n_tokens_mistral': 2609, 'n_tokens_neox': 2563, 'n_words': 627}