<commit_before>//! Draw textures using a projection matrix.

use gl;
use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;

use view::shaders;

/// Draw textures using a projection matrix.
pub struct T<'a> {
  #[allow(missing_docs)]
  pub shader: Shader<'a>,
}

#[allow(missing_docs)]
pub fn new<'a, 'b:'a>(gl: &'a GLContext, near: f32, far: f32) -> T<'b> {
  let components = vec!(
    (gl::VERTEX_SHADER, format!(r#"
      #version 330 core

      uniform mat4 projection_matrix;
      uniform vec3 eye_position;
      uniform float time_ms;

      uniform samplerBuffer positions;
      uniform samplerBuffer normals;

      in vec2 texture_position;
      in vec3 vertex_position;
      in int polygon_id;
      in uint tex_id;

      out vec2 vs_texture_position;
      out vec3 vs_normal;
      out float vs_tex_id;

      // include cnoise
      {}

      // include adjust_depth_precision
      {}

      mat3 between(vec3 v1, vec3 v2) {{
        vec3 v = cross(v1, v2);
        float s = length(v);
        float c = dot(v1, v2);
        mat3 skew =
          mat3(
            vec3(0, v.z, -v.y),
            vec3(-v.z, 0, v.x),
            vec3(v.y, -v.x, 0)
          );
        return mat3(1) + skew + skew*skew*(1-c)/(s*s);
      }}

      vec3 vec3Fetch(samplerBuffer vs, int float_idx) {{
        vec3 r;
        r.x = texelFetch(vs, float_idx + 0).r;
        r.y = texelFetch(vs, float_idx + 1).r;
        r.z = texelFetch(vs, float_idx + 2).r;
        return r;
      }}

      // compute a model-space shear to transform (0,1,0) to a desired vector.
      mat4 shearTo(vec3 desired) {{
        mat4 r = mat4(1);

        float d = dot(desired, vec3(0, 1, 0));
        // tweak the dot product to be in a valid range
        float new_d = exp(d - 1.0);
        // the common shear factor
        float k = 0;
        if (d < 0.99) {{
          k = sqrt((1.0-new_d*new_d)/(new_d*new_d*(1.0-d*d)));
        }}

        r[1].x = desired.x * k;
        r[1].y = 1.0;
        r[1].z = desired.z * k;

        return r;
      }}

      void main() {{
        vs_texture_position = texture_position;
        vs_tex_id = float(tex_id);

        // Put the grass tuft in the middle of the underlying terrain polygon.
        int position_id = polygon_id * 3 * 3;
        mat3 vertices =
          mat3(
            vec3Fetch(positions, position_id),
            vec3Fetch(positions, position_id + 3),
            vec3Fetch(positions, position_id + 6)
          );
        vec3 root = vertices * vec3(1.0/3.0);

        // Find the normal for the grass by barycentrically interpolating the
        // vertex normals to the root.
        int normal_id = polygon_id * 3 * 3;
        mat3 vertex_normals =
          mat3(
            vec3Fetch(normals, normal_id),
            vec3Fetch(normals, normal_id + 3),
            vec3Fetch(normals, normal_id + 6)
          );
        vec3 normal = vertex_normals * vec3(1.0/3.0);

        mat4 translation = mat4(1.0);
        translation[3].xyz = root;

        mat3 rotate_normal = between(vec3(0, 1, 0), normal);
        mat4 rotation = mat4(rotate_normal);

        mat4 shear = shearTo(inverse(rotate_normal) * vec3(0, 1, 0));

        mat4 noise_shear;
        {{
          float azimuth = 3.14 * cnoise(root + vec3(122, -1, 14.5) + vec3(0, time_ms / 2000, 0));
          float altitude = 3.14/2 - 3.14/4 * (cnoise(root + vec3(-18.11, 101.1, 44.5) + vec3(0, time_ms / 2000, 0)) + 1) / 2.0;
          vec3 v =
            vec3(
              cos(altitude) * cos(azimuth),
              sin(altitude),
              cos(altitude) * sin(azimuth)
            );
          noise_shear = shearTo(v);
        }}

        mat4 scale = mat4(1.0);
        float distance_scaling = exp(length(root - eye_position) / (1 << 6));
        scale[1].y = distance_scaling * 0.4;
        scale[0].x = distance_scaling * 4.0;
        scale[2].z = scale[0].x;

        mat4 model_matrix = translation * rotation * shear * noise_shear * scale;

        gl_Position =
          adjust_depth_precision(
            projection_matrix *
            model_matrix *
            vec4(vertex_position, 1)
          );

        vs_normal = normal;
      }}"#,
      shaders::adjust_depth_precision::as_string(near, far),
      shaders::noise::cnoise(),
    )),
    (gl::FRAGMENT_SHADER, format!("
      #version 330 core

      uniform struct Sun {{
        vec3 direction;
        vec3 intensity;
      }} sun;

      uniform vec3 ambient_light;
      uniform vec3 eye_position;

      uniform sampler2D texture_in;
      uniform float alpha_threshold;

      in vec2 vs_texture_position;
      in vec3 vs_normal;
      in float vs_tex_id;

      out vec4 frag_color;

      // depth fog
      {}

      // world fragment shading
      {}

      void main() {{
        int tex_id = int(round(vs_tex_id));
        int y = tex_id / 3;
        int x = tex_id % 3;
        vec2 tex_position = (vs_texture_position + y*vec2(0, 1) + x*vec2(1, 0)) / 3;
        vec4 c = texture(texture_in, tex_position);
        if (c.a < alpha_threshold) {{
          discard;
        }}
        vec3 world_position = vec3(gl_FragCoord.xy * gl_FragCoord.w, gl_FragCoord.w);
        vec4 fog_color = vec4(sun.intensity, 1);
        frag_color =
          world_fragment(
            sun.direction,
            sun.intensity,
            normalize(world_position - eye_position),
            ambient_light,
            c,
            1.0 / 0.0,
            vs_normal,
            fog_color,
            gl_FragCoord.z / gl_FragCoord.w
          );
      }}",
      shaders::depth_fog::to_string(),
      shaders::world_fragment::to_string(),
    )),
  );
  T {
    shader: Shader::new(gl, components.into_iter()),
  }
}
<commit_msg>scale grass to terrain polygon size<commit_after>//! Draw textures using a projection matrix.

use gl;
use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;

use view::shaders;

/// Draw textures using a projection matrix.
pub struct T<'a> {
  #[allow(missing_docs)]
  pub shader: Shader<'a>,
}

#[allow(missing_docs)]
pub fn new<'a, 'b:'a>(gl: &'a GLContext, near: f32, far: f32) -> T<'b> {
  let components = vec!(
    (gl::VERTEX_SHADER, format!(r#"
      #version 330 core

      uniform mat4 projection_matrix;
      uniform vec3 eye_position;
      uniform float time_ms;

      uniform samplerBuffer positions;
      uniform samplerBuffer normals;

      in vec2 texture_position;
      in vec3 vertex_position;
      in int polygon_id;
      in uint tex_id;

      out vec2 vs_texture_position;
      out vec3 vs_normal;
      out float vs_tex_id;

      // include cnoise
      {}

      // include adjust_depth_precision
      {}

      mat3 between(vec3 v1, vec3 v2) {{
        vec3 v = cross(v1, v2);
        float s = length(v);
        float c = dot(v1, v2);
        mat3 skew =
          mat3(
            vec3(0, v.z, -v.y),
            vec3(-v.z, 0, v.x),
            vec3(v.y, -v.x, 0)
          );
        return mat3(1) + skew + skew*skew*(1-c)/(s*s);
      }}

      vec3 vec3Fetch(samplerBuffer vs, int float_idx) {{
        vec3 r;
        r.x = texelFetch(vs, float_idx + 0).r;
        r.y = texelFetch(vs, float_idx + 1).r;
        r.z = texelFetch(vs, float_idx + 2).r;
        return r;
      }}

      // compute a model-space shear to transform (0,1,0) to a desired vector.
      mat4 shearTo(vec3 desired) {{
        mat4 r = mat4(1);

        float d = dot(desired, vec3(0, 1, 0));
        // tweak the dot product to be in a valid range
        float new_d = exp(d - 1.0);
        // the common shear factor
        float k = 0;
        if (d < 0.99) {{
          k = sqrt((1.0-new_d*new_d)/(new_d*new_d*(1.0-d*d)));
        }}

        r[1].x = desired.x * k;
        r[1].y = 1.0;
        r[1].z = desired.z * k;

        return r;
      }}

      void main() {{
        vs_texture_position = texture_position;
        vs_tex_id = float(tex_id);

        // Put the grass tuft in the middle of the underlying terrain polygon.
        int position_id = polygon_id * 3 * 3;
        mat3 vertices =
          mat3(
            vec3Fetch(positions, position_id),
            vec3Fetch(positions, position_id + 3),
            vec3Fetch(positions, position_id + 6)
          );
        vec3 side_length =
          vec3(
            length(vertices[0] - vertices[1]),
            length(vertices[1] - vertices[2]),
            length(vertices[2] - vertices[0])
          );
        vec3 root = vertices * vec3(1.0/3.0);

        // Find the normal for the grass by barycentrically interpolating the
        // vertex normals to the root.
        int normal_id = polygon_id * 3 * 3;
        mat3 vertex_normals =
          mat3(
            vec3Fetch(normals, normal_id),
            vec3Fetch(normals, normal_id + 3),
            vec3Fetch(normals, normal_id + 6)
          );
        vec3 normal = vertex_normals * vec3(1.0/3.0);

        mat4 translation = mat4(1.0);
        translation[3].xyz = root;

        mat3 rotate_normal = between(vec3(0, 1, 0), normal);
        mat4 rotation = mat4(rotate_normal);

        mat4 shear = shearTo(inverse(rotate_normal) * vec3(0, 1, 0));

        mat4 noise_shear;
        {{
          float azimuth = 3.14 * cnoise(root + vec3(122, -1, 14.5) + vec3(0, time_ms / 2000, 0));
          float altitude = 3.14/2 - 3.14/4 * (cnoise(root + vec3(-18.11, 101.1, 44.5) + vec3(0, time_ms / 2000, 0)) + 1) / 2.0;
          vec3 v =
            vec3(
              cos(altitude) * cos(azimuth),
              sin(altitude),
              cos(altitude) * sin(azimuth)
            );
          noise_shear = shearTo(v);
        }}

        mat4 scale = mat4(1.0);
        float max_side = max(max(side_length[0], side_length[1]), side_length[2]);
        float min_side = min(min(side_length[0], side_length[1]), side_length[2]);
        float side_scale = sqrt(min_side*max_side);
        scale[1].y = side_scale * 0.4;
        scale[0].x = side_scale * 4.0;
        scale[2].z = scale[0].x;

        mat4 model_matrix = translation * rotation * shear * noise_shear * scale;

        gl_Position =
          adjust_depth_precision(
            projection_matrix *
            model_matrix *
            vec4(vertex_position, 1)
          );

        vs_normal = normal;
      }}"#,
      shaders::adjust_depth_precision::as_string(near, far),
      shaders::noise::cnoise(),
    )),
    (gl::FRAGMENT_SHADER, format!("
      #version 330 core

      uniform struct Sun {{
        vec3 direction;
        vec3 intensity;
      }} sun;

      uniform vec3 ambient_light;
      uniform vec3 eye_position;

      uniform sampler2D texture_in;
      uniform float alpha_threshold;

      in vec2 vs_texture_position;
      in vec3 vs_normal;
      in float vs_tex_id;

      out vec4 frag_color;

      // depth fog
      {}

      // world fragment shading
      {}

      void main() {{
        int tex_id = int(round(vs_tex_id));
        int y = tex_id / 3;
        int x = tex_id % 3;
        vec2 tex_position = (vs_texture_position + y*vec2(0, 1) + x*vec2(1, 0)) / 3;
        vec4 c = texture(texture_in, tex_position);
        if (c.a < alpha_threshold) {{
          discard;
        }}
        vec3 world_position = vec3(gl_FragCoord.xy * gl_FragCoord.w, gl_FragCoord.w);
        vec4 fog_color = vec4(sun.intensity, 1);
        frag_color =
          world_fragment(
            sun.direction,
            sun.intensity,
            normalize(world_position - eye_position),
            ambient_light,
            c,
            1.0 / 0.0,
            vs_normal,
            fog_color,
            gl_FragCoord.z / gl_FragCoord.w
          );
      }}",
      shaders::depth_fog::to_string(),
      shaders::world_fragment::to_string(),
    )),
  );
  T {
    shader: Shader::new(gl, components.into_iter()),
  }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Seek};
use std::path::Path;
use std::u32;

#[cfg(feature = "bmp")]
use crate::codecs::bmp;
#[cfg(feature = "gif")]
use crate::codecs::gif;
#[cfg(feature = "hdr")]
use crate::codecs::hdr;
#[cfg(feature = "ico")]
use crate::codecs::ico;
#[cfg(feature = "jpeg")]
use crate::codecs::jpeg;
#[cfg(feature = "png")]
use crate::codecs::png;
#[cfg(feature = "pnm")]
use crate::codecs::pnm;
#[cfg(feature = "tga")]
use crate::codecs::tga;
#[cfg(feature = "dds")]
use crate::codecs::dds;
#[cfg(feature = "tiff")]
use crate::codecs::tiff;
#[cfg(feature = "webp")]
use crate::codecs::webp;
#[cfg(feature = "farbfeld")]
use crate::codecs::farbfeld;
#[cfg(feature = "avif")]
use crate::avif;

use crate::color;
use crate::image;
use crate::dynimage::DynamicImage;
use crate::error::{ImageError, ImageFormatHint, ImageResult};
use crate::image::ImageFormat;
#[allow(unused_imports)]  // When no features are supported
use crate::image::{ImageDecoder, ImageEncoder};

pub(crate) fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
    let fin = match File::open(path) {
        Ok(f) => f,
        Err(err) => return Err(ImageError::IoError(err)),
    };
    let fin = BufReader::new(fin);

    load(fin, ImageFormat::from_path(path)?)
}

/// Create a new image from a Reader
///
/// Try [`io::Reader`] for more advanced uses.
///
/// [`io::Reader`]: io/struct.Reader.html
#[allow(unused_variables)]
// r is unused if no features are supported.
pub fn load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
    #[allow(unreachable_patterns)]
    // Default is unreachable if all features are supported.
    match format {
        #[cfg(feature = "png")]
        image::ImageFormat::Png => DynamicImage::from_decoder(png::PngDecoder::new(r)?),
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => DynamicImage::from_decoder(gif::GifDecoder::new(r)?),
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => DynamicImage::from_decoder(jpeg::JpegDecoder::new(r)?),
        #[cfg(feature = "webp")]
        image::ImageFormat::WebP => DynamicImage::from_decoder(webp::WebPDecoder::new(r)?),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => DynamicImage::from_decoder(tiff::TiffDecoder::new(r)?),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => DynamicImage::from_decoder(tga::TgaDecoder::new(r)?),
        #[cfg(feature = "dds")]
        image::ImageFormat::Dds => DynamicImage::from_decoder(dds::DdsDecoder::new(r)?),
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => DynamicImage::from_decoder(bmp::BmpDecoder::new(r)?),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => DynamicImage::from_decoder(ico::IcoDecoder::new(r)?),
        #[cfg(feature = "hdr")]
        image::ImageFormat::Hdr => DynamicImage::from_decoder(hdr::HdrAdapter::new(BufReader::new(r))?),
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => DynamicImage::from_decoder(pnm::PnmDecoder::new(BufReader::new(r))?),
        #[cfg(feature = "farbfeld")]
        image::ImageFormat::Farbfeld => DynamicImage::from_decoder(farbfeld::FarbfeldDecoder::new(r)?),
        _ => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
    }
}

pub(crate) fn image_dimensions_impl(path: &Path) -> ImageResult<(u32, u32)> {
    let format = image::ImageFormat::from_path(path)?;

    let fin = File::open(path)?;
    let fin = BufReader::new(fin);

    image_dimensions_with_format_impl(fin, format)
}

#[allow(unused_variables)]
// fin is unused if no features are supported.
pub(crate) fn image_dimensions_with_format_impl<R: BufRead + Seek>(fin: R, format: ImageFormat)
    -> ImageResult<(u32, u32)>
{
    #[allow(unreachable_patterns,unreachable_code)]
    // Default is unreachable if all features are supported.
    // Code after the match is unreachable if none are.
    Ok(match format {
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => jpeg::JpegDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "png")]
        image::ImageFormat::Png => png::PngDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => gif::GifDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "webp")]
        image::ImageFormat::WebP => webp::WebPDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => tiff::TiffDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => tga::TgaDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "dds")]
        image::ImageFormat::Dds => dds::DdsDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => bmp::BmpDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => ico::IcoDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "hdr")]
        image::ImageFormat::Hdr => hdr::HdrAdapter::new(fin)?.dimensions(),
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => {
            pnm::PnmDecoder::new(fin)?.dimensions()
        }
        format => return Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
    })
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
) -> ImageResult<()> {
    let fout = &mut BufWriter::new(File::create(path)?);
    let ext = path.extension()
        .and_then(|s| s.to_str())
        .map_or("".to_string(), |s| s.to_ascii_lowercase());

    match &*ext {
        #[cfg(feature = "gif")]
        "gif" => gif::GifEncoder::new(fout).encode(buf, width, height, color),
        #[cfg(feature = "ico")]
        "ico" => ico::IcoEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "jpeg")]
        "jpg" | "jpeg" => jpeg::JpegEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "png")]
        "png" => png::PngEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "pnm")]
        "pbm" => pnm::PnmEncoder::new(fout)
            .with_subtype(pnm::PNMSubtype::Bitmap(pnm::SampleEncoding::Binary))
            .write_image(buf, width, height, color),
        #[cfg(feature = "pnm")]
        "pgm" => pnm::PnmEncoder::new(fout)
            .with_subtype(pnm::PNMSubtype::Graymap(pnm::SampleEncoding::Binary))
            .write_image(buf, width, height, color),
        #[cfg(feature = "pnm")]
        "ppm" => pnm::PnmEncoder::new(fout)
            .with_subtype(pnm::PNMSubtype::Pixmap(pnm::SampleEncoding::Binary))
            .write_image(buf, width, height, color),
        #[cfg(feature = "pnm")]
        "pam" => pnm::PnmEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "bmp")]
        "bmp" => bmp::BmpEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "tiff")]
        "tif" | "tiff" => tiff::TiffEncoder::new(fout)
            .write_image(buf, width, height, color),
        #[cfg(feature = "tga")]
        "tga" => tga::TgaEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "avif")]
        "avif" => avif::AvifEncoder::new(fout).write_image(buf, width, height, color),
        _ => Err(ImageError::Unsupported(ImageFormatHint::from(path).into())),
    }
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_with_format_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
    format: ImageFormat,
) -> ImageResult<()> {
    let fout = &mut BufWriter::new(File::create(path)?);

    match format {
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => gif::GifEncoder::new(fout).encode(buf, width, height, color),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => ico::IcoEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => jpeg::JpegEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "png")]
        image::ImageFormat::Png => png::PngEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => bmp::BmpEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => tiff::TiffEncoder::new(fout)
            .write_image(buf, width, height, color),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => tga::TgaEncoder::new(fout).write_image(buf, width, height, color),
        format => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
    }
}

static MAGIC_BYTES: [(&[u8], ImageFormat); 19] = [
    (b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
    (&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
    (b"GIF89a", ImageFormat::Gif),
    (b"GIF87a", ImageFormat::Gif),
    (b"RIFF", ImageFormat::WebP), // TODO: better magic byte detection, see https://github.com/image-rs/image/issues/660
    (b"MM\x00*", ImageFormat::Tiff),
    (b"II*\x00", ImageFormat::Tiff),
    (b"DDS ", ImageFormat::Dds),
    (b"BM", ImageFormat::Bmp),
    (&[0, 0, 1, 0], ImageFormat::Ico),
    (b"#?RADIANCE", ImageFormat::Hdr),
    (b"P1", ImageFormat::Pnm),
    (b"P2", ImageFormat::Pnm),
    (b"P3", ImageFormat::Pnm),
    (b"P4", ImageFormat::Pnm),
    (b"P5", ImageFormat::Pnm),
    (b"P6", ImageFormat::Pnm),
    (b"P7", ImageFormat::Pnm),
    (b"farbfeld", ImageFormat::Farbfeld),
];

/// Guess image format from memory block
///
/// Makes an educated guess about the image format based on the Magic Bytes at the beginning.
/// TGA is not supported by this function.
/// This is not to be trusted on the validity of the whole memory block
pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
    match guess_format_impl(buffer) {
        Some(format) => Ok(format),
        None => Err(ImageError::Unsupported(ImageFormatHint::Unknown.into())),
    }
}

pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {
    for &(signature, format) in &MAGIC_BYTES {
        if buffer.starts_with(signature) {
            return Some(format);
        }
    }

    None
}
<commit_msg>Fixes #1390: Refactors save_buffer_impl to call save_buffer_with_format_impl.<commit_after>use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Seek};
use std::path::Path;
use std::u32;

#[cfg(feature = "bmp")]
use crate::codecs::bmp;
#[cfg(feature = "gif")]
use crate::codecs::gif;
#[cfg(feature = "hdr")]
use crate::codecs::hdr;
#[cfg(feature = "ico")]
use crate::codecs::ico;
#[cfg(feature = "jpeg")]
use crate::codecs::jpeg;
#[cfg(feature = "png")]
use crate::codecs::png;
#[cfg(feature = "pnm")]
use crate::codecs::pnm;
#[cfg(feature = "tga")]
use crate::codecs::tga;
#[cfg(feature = "dds")]
use crate::codecs::dds;
#[cfg(feature = "tiff")]
use crate::codecs::tiff;
#[cfg(feature = "webp")]
use crate::codecs::webp;
#[cfg(feature = "farbfeld")]
use crate::codecs::farbfeld;
#[cfg(feature = "avif")]
use crate::avif;

use crate::color;
use crate::image;
use crate::dynimage::DynamicImage;
use crate::error::{ImageError, ImageFormatHint, ImageResult};
use crate::image::ImageFormat;
#[allow(unused_imports)]  // When no features are supported
use crate::image::{ImageDecoder, ImageEncoder};

pub(crate) fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
    let fin = match File::open(path) {
        Ok(f) => f,
        Err(err) => return Err(ImageError::IoError(err)),
    };
    let fin = BufReader::new(fin);

    load(fin, ImageFormat::from_path(path)?)
}

/// Create a new image from a Reader
///
/// Try [`io::Reader`] for more advanced uses.
///
/// [`io::Reader`]: io/struct.Reader.html
#[allow(unused_variables)]
// r is unused if no features are supported.
pub fn load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
    #[allow(unreachable_patterns)]
    // Default is unreachable if all features are supported.
    match format {
        #[cfg(feature = "png")]
        image::ImageFormat::Png => DynamicImage::from_decoder(png::PngDecoder::new(r)?),
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => DynamicImage::from_decoder(gif::GifDecoder::new(r)?),
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => DynamicImage::from_decoder(jpeg::JpegDecoder::new(r)?),
        #[cfg(feature = "webp")]
        image::ImageFormat::WebP => DynamicImage::from_decoder(webp::WebPDecoder::new(r)?),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => DynamicImage::from_decoder(tiff::TiffDecoder::new(r)?),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => DynamicImage::from_decoder(tga::TgaDecoder::new(r)?),
        #[cfg(feature = "dds")]
        image::ImageFormat::Dds => DynamicImage::from_decoder(dds::DdsDecoder::new(r)?),
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => DynamicImage::from_decoder(bmp::BmpDecoder::new(r)?),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => DynamicImage::from_decoder(ico::IcoDecoder::new(r)?),
        #[cfg(feature = "hdr")]
        image::ImageFormat::Hdr => DynamicImage::from_decoder(hdr::HdrAdapter::new(BufReader::new(r))?),
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => DynamicImage::from_decoder(pnm::PnmDecoder::new(BufReader::new(r))?),
        #[cfg(feature = "farbfeld")]
        image::ImageFormat::Farbfeld => DynamicImage::from_decoder(farbfeld::FarbfeldDecoder::new(r)?),
        _ => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
    }
}

pub(crate) fn image_dimensions_impl(path: &Path) -> ImageResult<(u32, u32)> {
    let format = image::ImageFormat::from_path(path)?;

    let fin = File::open(path)?;
    let fin = BufReader::new(fin);

    image_dimensions_with_format_impl(fin, format)
}

#[allow(unused_variables)]
// fin is unused if no features are supported.
pub(crate) fn image_dimensions_with_format_impl<R: BufRead + Seek>(fin: R, format: ImageFormat)
    -> ImageResult<(u32, u32)>
{
    #[allow(unreachable_patterns,unreachable_code)]
    // Default is unreachable if all features are supported.
    // Code after the match is unreachable if none are.
    Ok(match format {
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => jpeg::JpegDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "png")]
        image::ImageFormat::Png => png::PngDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => gif::GifDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "webp")]
        image::ImageFormat::WebP => webp::WebPDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => tiff::TiffDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => tga::TgaDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "dds")]
        image::ImageFormat::Dds => dds::DdsDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => bmp::BmpDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => ico::IcoDecoder::new(fin)?.dimensions(),
        #[cfg(feature = "hdr")]
        image::ImageFormat::Hdr => hdr::HdrAdapter::new(fin)?.dimensions(),
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => {
            pnm::PnmDecoder::new(fin)?.dimensions()
        }
        format => return Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
    })
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
) -> ImageResult<()> {
    let fout = &mut BufWriter::new(File::create(path)?);
    let format =  ImageFormat::from_path(path)?;
    save_buffer_with_format_impl(path, buf, width, height, color, format)
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_with_format_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
    format: ImageFormat,
) -> ImageResult<()> {
    let fout = &mut BufWriter::new(File::create(path)?);

    match format {
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => gif::GifEncoder::new(fout).encode(buf, width, height, color),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => ico::IcoEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => jpeg::JpegEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "png")]
        image::ImageFormat::Png => png::PngEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => {
            let ext = path.extension()
            .and_then(|s| s.to_str())
            .map_or("".to_string(), |s| s.to_ascii_lowercase());
            match &*ext {
                "pbm" => pnm::PnmEncoder::new(fout)
                    .with_subtype(pnm::PNMSubtype::Bitmap(pnm::SampleEncoding::Binary))
                    .write_image(buf, width, height, color),
                "pgm" => pnm::PnmEncoder::new(fout)
                    .with_subtype(pnm::PNMSubtype::Graymap(pnm::SampleEncoding::Binary))
                    .write_image(buf, width, height, color),
                "ppm" => pnm::PnmEncoder::new(fout)
                    .with_subtype(pnm::PNMSubtype::Pixmap(pnm::SampleEncoding::Binary))
                    .write_image(buf, width, height, color),
                "pam" => pnm::PnmEncoder::new(fout).write_image(buf, width, height, color),
                _ => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())), // Unsupported Pnm subtype.
            }
        },
        #[cfg(feature = "farbfeld")]
        image::ImageFormat::Farbfeld => farbfeld::FarbfeldEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "avif")]
        image::ImageFormat::Avif => avif::AvifEncoder::new(fout).write_image(buf, width, height, color),
        // #[cfg(feature = "hdr")]
        // image::ImageFormat::Hdr => hdr::HdrEncoder::new(fout).encode(&[Rgb<f32>], width, height), // usize
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => bmp::BmpEncoder::new(fout).write_image(buf, width, height, color),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => tiff::TiffEncoder::new(fout)
            .write_image(buf, width, height, color),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => tga::TgaEncoder::new(fout).write_image(buf, width, height, color),
        format => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
    }
}

static MAGIC_BYTES: [(&[u8], ImageFormat); 19] = [
    (b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
    (&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
    (b"GIF89a", ImageFormat::Gif),
    (b"GIF87a", ImageFormat::Gif),
    (b"RIFF", ImageFormat::WebP), // TODO: better magic byte detection, see https://github.com/image-rs/image/issues/660
    (b"MM\x00*", ImageFormat::Tiff),
    (b"II*\x00", ImageFormat::Tiff),
    (b"DDS ", ImageFormat::Dds),
    (b"BM", ImageFormat::Bmp),
    (&[0, 0, 1, 0], ImageFormat::Ico),
    (b"#?RADIANCE", ImageFormat::Hdr),
    (b"P1", ImageFormat::Pnm),
    (b"P2", ImageFormat::Pnm),
    (b"P3", ImageFormat::Pnm),
    (b"P4", ImageFormat::Pnm),
    (b"P5", ImageFormat::Pnm),
    (b"P6", ImageFormat::Pnm),
    (b"P7", ImageFormat::Pnm),
    (b"farbfeld", ImageFormat::Farbfeld),
];

/// Guess image format from memory block
///
/// Makes an educated guess about the image format based on the Magic Bytes at the beginning.
/// TGA is not supported by this function.
/// This is not to be trusted on the validity of the whole memory block
pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
    match guess_format_impl(buffer) {
        Some(format) => Ok(format),
        None => Err(ImageError::Unsupported(ImageFormatHint::Unknown.into())),
    }
}

pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {
    for &(signature, format) in &MAGIC_BYTES {
        if buffer.starts_with(signature) {
            return Some(format);
        }
    }

    None
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{idhasher::BuildIdHasher, intern_struct, string, InternId, InternSerdes};
use serde_derive::{Deserialize, Serialize};
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::{
    cmp::Ordering,
    ffi::OsStr,
    path::{Path, PathBuf},
};

intern_struct! {
    /// A path whose path components are interned.
    pub struct PathId = Intern<PathNode> {
        serdes("InternSerdes<PathId>");
        type Set = PathIdSet;
        type Map = PathIdMap;
    }
}

pub type PathIdIndexMap<V> = indexmap::IndexMap<PathId, V, BuildIdHasher<u32>>;
pub type PathIdIndexSet = indexmap::IndexSet<PathId, BuildIdHasher<u32>>;

impl PathId {
    /// Intern the given path one `std::path::Component` at a time using the
    /// `Path::components()` iterator, which normalizes during parsing.  Panic
    /// on empty paths such as `/`.
    #[cfg(unix)]
    pub fn intern<P: AsRef<Path>>(mut parent: Option<PathId>, path: P) -> Self {
        for c in path.as_ref().iter() {
            let p = InternId::intern(PathNode {
                name: string::intern_bytes(c.as_bytes()),
                parent,
            });
            parent = Some(p);
        }
        parent.unwrap()
    }

    /// Intern the given path one `std::path::Component` at a time using the
    /// `Path::components()` iterator, which normalizes during parsing.  Panic
    /// on empty paths such as `/`.
    #[cfg(not(unix))]
    pub fn intern<P: AsRef<Path>>(mut parent: Option<PathId>, path: P) -> Self {
        for c in path.as_ref().iter() {
            let p = InternId::intern(PathNode {
                name: string::intern(c.to_str().unwrap()),
                parent,
            });
            parent = Some(p);
        }
        parent.unwrap()
    }

    pub fn parent(&self) -> Option<PathId> {
        self.parent
    }

    /// Returns the final component as an &OsStr.
    pub fn file_name(&self) -> &OsStr {
        self.get().file_name()
    }

    /// Linearize this path as a PathBuf.
    pub fn to_path_buf(&self) -> PathBuf {
        self.get().to_path_buf()
    }
}

impl<P: AsRef<Path>> From<P> for PathId {
    fn from(path: P) -> Self {
        Self::intern(None, path)
    }
}

impl From<PathId> for PathBuf {
    fn from(id: PathId) -> Self {
        id.to_path_buf()
    }
}

impl std::fmt::Display for PathId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_path_buf().display().fmt(f)
    }
}

impl std::cmp::Ord for PathId {
    /// Compare the linearized components of self to other in top-down order.
    fn cmp(&self, other: &Self) -> Ordering {
        type LinearPath = smallvec::SmallVec<[PathComponentId; 20]>;
        fn linearize(mut p: PathId) -> LinearPath {
            let mut v: LinearPath = Default::default();
            v.push(p.name);
            while let Some(parent) = p.parent {
                v.push(parent.name);
                p = parent;
            }
            v
        }
        linearize(*self)
            .iter()
            .rev()
            .cmp(linearize(*other).iter().rev())
    }
}

impl std::cmp::PartialOrd for PathId {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(unix)]
type PathComponentId = string::BytesId;

#[cfg(not(unix))]
type PathComponentId = string::StringId;

/// A `PathNode` represents a `Path` as a list, starting with a leaf component
/// and walking up the directory tree.  This is really an internal detail, but
/// is exposed by `PathId::Intern`.
#[doc(hidden)]
#[derive(Hash, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub struct PathNode {
    name: PathComponentId,
    parent: Option<PathId>,
}

impl PathNode {
    /// Returns the final component as an &OsStr.
    #[cfg(unix)]
    fn file_name(&self) -> &OsStr {
        OsStr::from_bytes(self.name.as_bytes())
    }

    /// Returns the final component as an &OsStr.
    #[cfg(not(unix))]
    fn file_name(&self) -> &OsStr {
        OsStr::new(self.name.as_str())
    }

    /// Linearize this path as a PathBuf.
    fn to_path_buf(&self) -> PathBuf {
        let mut path = match self.parent {
            Some(parent) => parent.to_path_buf(),
            None => PathBuf::new(),
        };
        path.push(self.file_name());
        path
    }
}

impl std::fmt::Debug for PathNode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_path_buf().fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_eq() {
        assert_eq!(PathId::from("a"), PathId::from("a/"));
        assert_ne!(PathId::from("a"), PathId::from("/a"));
        assert_eq!(PathId::from("a/b"), PathId::from("a//b"));
    }

    #[test]
    fn test_cmp() {
        assert_eq!(PathId::from("a").cmp(&PathId::from("a")), Ordering::Equal);
        assert_eq!(PathId::from("a").cmp(&PathId::from("b")), Ordering::Less);
        assert_eq!(PathId::from("a").cmp(&PathId::from("a/a")), Ordering::Less);
        assert_eq!(PathId::from("a").cmp(&PathId::from("a/_")), Ordering::Less);
        assert_eq!(PathId::from("b").cmp(&PathId::from("a")), Ordering::Greater);
        assert_eq!(
            PathId::from("a/a").cmp(&PathId::from("a")),
            Ordering::Greater
        );
        assert_eq!(
            PathId::from("a/_").cmp(&PathId::from("a")),
            Ordering::Greater
        );
        assert_eq!(
            PathId::from("a/b").cmp(&PathId::from("a.b")),
            Ordering::Less
        );
    }

    #[test]
    fn test_intern() {
        assert_eq!(PathId::from("a/b"), PathId::intern(None, "a/b"));
        assert_eq!(
            PathId::from("a/b/c"),
            PathId::intern(Some(PathId::from("a")), "b/c"),
        );
    }
}
<commit_msg>Intern RelativePath using intern::PathId<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{idhasher::BuildIdHasher, intern_struct, string, InternId, InternSerdes};
use serde_derive::{Deserialize, Serialize};
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::{
    cmp::Ordering,
    ffi::OsStr,
    path::{Path, PathBuf},
};

intern_struct! {
    /// A path whose path components are interned.
    pub struct PathId = Intern<PathNode> {
        serdes("InternSerdes<PathId>");
        type Set = PathIdSet;
        type Map = PathIdMap;
    }
}

pub type PathIdIndexMap<V> = indexmap::IndexMap<PathId, V, BuildIdHasher<u32>>;
pub type PathIdIndexSet = indexmap::IndexSet<PathId, BuildIdHasher<u32>>;

impl PathId {
    /// Intern the given path one `std::path::Component` at a time using the
    /// `Path::components()` iterator, which normalizes during parsing.  Panic
    /// on empty paths such as `/`.
    #[cfg(unix)]
    pub fn intern<P: AsRef<Path>>(mut parent: Option<PathId>, path: P) -> Self {
        for c in path.as_ref().iter() {
            let p = InternId::intern(PathNode {
                name: string::intern_bytes(c.as_bytes()),
                parent,
            });
            parent = Some(p);
        }
        parent.unwrap()
    }

    /// Intern the given path one `std::path::Component` at a time using the
    /// `Path::components()` iterator, which normalizes during parsing.  Panic
    /// on empty paths such as `/`.
    #[cfg(not(unix))]
    pub fn intern<P: AsRef<Path>>(mut parent: Option<PathId>, path: P) -> Self {
        for c in path.as_ref().iter() {
            let p = InternId::intern(PathNode {
                name: string::intern(c.to_str().unwrap()),
                parent,
            });
            parent = Some(p);
        }
        parent.unwrap()
    }

    pub fn parent(&self) -> Option<PathId> {
        self.parent
    }

    /// Returns the final component as an &OsStr.
    pub fn file_name(&self) -> &OsStr {
        self.get().file_name()
    }

    /// Linearize this path as a PathBuf.
    pub fn to_path_buf(&self) -> PathBuf {
        self.get().to_path_buf()
    }

    /// Linearize this path, appending to an existing PathBuf.
    pub fn push_to(&self, buf: &mut PathBuf) {
        self.get().push_to(buf);
    }
}

impl<P: AsRef<Path>> From<P> for PathId {
    fn from(path: P) -> Self {
        Self::intern(None, path)
    }
}

impl From<PathId> for PathBuf {
    fn from(id: PathId) -> Self {
        id.to_path_buf()
    }
}

impl std::fmt::Display for PathId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_path_buf().display().fmt(f)
    }
}

impl std::cmp::Ord for PathId {
    /// Compare the linearized components of self to other in top-down order.
    fn cmp(&self, other: &Self) -> Ordering {
        type LinearPath = smallvec::SmallVec<[PathComponentId; 20]>;
        fn linearize(mut p: PathId) -> LinearPath {
            let mut v: LinearPath = Default::default();
            v.push(p.name);
            while let Some(parent) = p.parent {
                v.push(parent.name);
                p = parent;
            }
            v
        }
        linearize(*self)
            .iter()
            .rev()
            .cmp(linearize(*other).iter().rev())
    }
}

impl std::cmp::PartialOrd for PathId {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(unix)]
type PathComponentId = string::BytesId;

#[cfg(not(unix))]
type PathComponentId = string::StringId;

/// A `PathNode` represents a `Path` as a list, starting with a leaf component
/// and walking up the directory tree.  This is really an internal detail, but
/// is exposed by `PathId::Intern`.
#[doc(hidden)]
#[derive(Hash, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub struct PathNode {
    name: PathComponentId,
    parent: Option<PathId>,
}

impl PathNode {
    /// Returns the final component as an &OsStr.
    #[cfg(unix)]
    fn file_name(&self) -> &OsStr {
        OsStr::from_bytes(self.name.as_bytes())
    }

    /// Returns the final component as an &OsStr.
    #[cfg(not(unix))]
    fn file_name(&self) -> &OsStr {
        OsStr::new(self.name.as_str())
    }

    /// Linearize this path as a PathBuf.
    fn to_path_buf(&self) -> PathBuf {
        let mut path = match self.parent {
            Some(parent) => parent.to_path_buf(),
            None => PathBuf::new(),
        };
        path.push(self.file_name());
        path
    }

    /// Linearize this path, appending to an existing PathBuf.
    fn push_to(&self, buf: &mut PathBuf) {
        if let Some(parent) = self.parent {
            parent.push_to(buf);
        }
        buf.push(self.file_name());
    }
}

impl std::fmt::Debug for PathNode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_path_buf().fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_eq() {
        assert_eq!(PathId::from("a"), PathId::from("a/"));
        assert_ne!(PathId::from("a"), PathId::from("/a"));
        assert_eq!(PathId::from("a/b"), PathId::from("a//b"));
    }

    #[test]
    fn test_cmp() {
        assert_eq!(PathId::from("a").cmp(&PathId::from("a")), Ordering::Equal);
        assert_eq!(PathId::from("a").cmp(&PathId::from("b")), Ordering::Less);
        assert_eq!(PathId::from("a").cmp(&PathId::from("a/a")), Ordering::Less);
        assert_eq!(PathId::from("a").cmp(&PathId::from("a/_")), Ordering::Less);
        assert_eq!(PathId::from("b").cmp(&PathId::from("a")), Ordering::Greater);
        assert_eq!(
            PathId::from("a/a").cmp(&PathId::from("a")),
            Ordering::Greater
        );
        assert_eq!(
            PathId::from("a/_").cmp(&PathId::from("a")),
            Ordering::Greater
        );
        assert_eq!(
            PathId::from("a/b").cmp(&PathId::from("a.b")),
            Ordering::Less
        );
    }

    #[test]
    fn test_intern() {
        assert_eq!(PathId::from("a/b"), PathId::intern(None, "a/b"));
        assert_eq!(
            PathId::from("a/b/c"),
            PathId::intern(Some(PathId::from("a")), "b/c"),
        );
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub static rc_base_field_refcnt: uint = 0u;

pub static task_field_refcnt: uint = 0u;

pub static task_field_stk: uint = 2u;

pub static task_field_runtime_sp: uint = 3u;

pub static task_field_rust_sp: uint = 4u;

pub static task_field_gc_alloc_chain: uint = 5u;

pub static task_field_dom: uint = 6u;

pub static n_visible_task_fields: uint = 7u;

pub static dom_field_interrupt_flag: uint = 1u;

pub static frame_glue_fns_field_mark: uint = 0u;

pub static frame_glue_fns_field_drop: uint = 1u;

pub static frame_glue_fns_field_reloc: uint = 2u;

pub static box_field_refcnt: uint = 0u;
pub static box_field_tydesc: uint = 1u;
pub static box_field_prev: uint = 2u;
pub static box_field_next: uint = 3u;
pub static box_field_body: uint = 4u;

pub static general_code_alignment: uint = 16u;

pub static tydesc_field_size: uint = 0u;
pub static tydesc_field_align: uint = 1u;
pub static tydesc_field_drop_glue: uint = 2u;
pub static tydesc_field_visit_glue: uint = 3u;
pub static tydesc_field_name_offset: uint = 4u;
pub static n_tydesc_fields: uint = 5u;

// The two halves of a closure: code and environment.
pub static fn_field_code: uint = 0u;
pub static fn_field_box: uint = 1u;

// The two fields of a trait object/trait instance: vtable and box.
// The vtable contains the type descriptor as first element.
pub static trt_field_vtable: uint = 0u;
pub static trt_field_box: uint = 1u;

pub static vec_elt_fill: uint = 0u;

pub static vec_elt_alloc: uint = 1u;

pub static vec_elt_elems: uint = 2u;

pub static slice_elt_base: uint = 0u;
pub static slice_elt_len: uint = 1u;

pub static abi_version: uint = 1u;
<commit_msg>back: abi: remove dead code<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub static box_field_refcnt: uint = 0u;
pub static box_field_tydesc: uint = 1u;
pub static box_field_body: uint = 4u;

pub static tydesc_field_visit_glue: uint = 3u;

// The two halves of a closure: code and environment.
pub static fn_field_code: uint = 0u;
pub static fn_field_box: uint = 1u;

// The two fields of a trait object/trait instance: vtable and box.
// The vtable contains the type descriptor as first element.
pub static trt_field_vtable: uint = 0u;
pub static trt_field_box: uint = 1u;

pub static vec_elt_fill: uint = 0u;

pub static vec_elt_alloc: uint = 1u;

pub static vec_elt_elems: uint = 2u;

pub static slice_elt_base: uint = 0u;
pub static slice_elt_len: uint = 1u;
<|endoftext|>


<commit_before>/*
Module: generic_os

Some miscellaneous platform functions.

These should be rolled into another module.
*/

import core::option;

// Wow, this is an ugly way to write doc comments

#[cfg(bogus)]
#[doc = "Get the value of an environment variable"]
fn getenv(n: str) -> option<str> { }

#[cfg(bogus)]
#[doc = "Set the value of an environment variable"]
fn setenv(n: str, v: str) { }

fn env() -> [(str,str)] {
    let pairs = [];
    for p in os::rustrt::rust_env_pairs() {
        let vs = str::split_char(p, '=');
        assert vec::len(vs) == 2u;
        pairs += [(vs[0], vs[1])];
    }
    ret pairs;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn getenv(n: str) -> option<str> unsafe {
    let s = str::as_buf(n, {|buf| os::libc::getenv(buf) });
    ret if unsafe::reinterpret_cast(s) == 0 {
            option::none::<str>
        } else {
            let s = unsafe::reinterpret_cast(s);
            option::some::<str>(str::from_cstr(s))
        };
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn setenv(n: str, v: str) {
    // FIXME (868)
    str::as_buf(
        n,
        // FIXME (868)
        {|nbuf|
            str::as_buf(
                v,
                {|vbuf|
                    os::libc::setenv(nbuf, vbuf, 1i32)})});
}

#[cfg(target_os = "win32")]
fn getenv(n: str) -> option<str> {
    let nsize = 256u;
    while true {
        let v: [u8] = [];
        vec::reserve(v, nsize);
        let res =
            str::as_buf(n,
                        {|nbuf|
                            unsafe {
                            let vbuf = vec::to_ptr(v);
                            os::kernel32::GetEnvironmentVariableA(nbuf, vbuf,
                                                                  nsize)
                        }
                        });
        if res == 0u {
            ret option::none;
        } else if res < nsize {
            unsafe {
                vec::unsafe::set_len(v, res);
            }
            ret option::some(str::from_bytes(v)); // UTF-8 or fail
        } else { nsize = res; }
    }
    fail;
}

#[cfg(target_os = "win32")]
fn setenv(n: str, v: str) {
    // FIXME (868)
    let _: () =
        str::as_buf(n, {|nbuf|
            let _: () =
                str::as_buf(v, {|vbuf|
                    os::kernel32::SetEnvironmentVariableA(nbuf, vbuf);
                });
        });
}

#[cfg(test)]
mod tests {

    #[test]
    #[ignore(reason = "fails periodically on mac")]
    fn test_setenv() {
        // NB: Each test of setenv needs to use different variable names or
        // the tests will not be threadsafe
        setenv("NAME1", "VALUE");
        assert (getenv("NAME1") == option::some("VALUE"));
    }

    #[test]
    #[ignore(reason = "fails periodically on mac")]
    fn test_setenv_overwrite() {
        setenv("NAME2", "1");
        setenv("NAME2", "2");
        assert (getenv("NAME2") == option::some("2"));
    }

    // Windows GetEnvironmentVariable requires some extra work to make sure
    // the buffer the variable is copied into is the right size
    #[test]
    #[ignore(reason = "fails periodically on mac")]
    fn test_getenv_big() {
        let s = "";
        let i = 0;
        while i < 100 { s += "aaaaaaaaaa"; i += 1; }
        setenv("test_getenv_big", s);
        log(debug, s);
        assert (getenv("test_getenv_big") == option::some(s));
    }

    #[test]
    fn test_get_exe_path() {
        let path = os::get_exe_path();
        assert option::is_some(path);
        let path = option::get(path);
        log(debug, path);

        // Hard to test this function
        if os::target_os() != "win32" {
            assert str::starts_with(path, fs::path_sep());
        } else {
            assert path[1] == ':' as u8;
        }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>std: Fix env() assert when an environment variable's value contains a '=' char<commit_after>/*
Module: generic_os

Some miscellaneous platform functions.

These should be rolled into another module.
*/

import core::option;

// Wow, this is an ugly way to write doc comments

#[cfg(bogus)]
#[doc = "Get the value of an environment variable"]
fn getenv(n: str) -> option<str> { }

#[cfg(bogus)]
#[doc = "Set the value of an environment variable"]
fn setenv(n: str, v: str) { }

fn env() -> [(str,str)] {
    let pairs = [];
    for p in os::rustrt::rust_env_pairs() {
        let vs = str::splitn_char(p, '=', 1u);
        assert vec::len(vs) == 2u;
        pairs += [(vs[0], vs[1])];
    }
    ret pairs;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn getenv(n: str) -> option<str> unsafe {
    let s = str::as_buf(n, {|buf| os::libc::getenv(buf) });
    ret if unsafe::reinterpret_cast(s) == 0 {
            option::none::<str>
        } else {
            let s = unsafe::reinterpret_cast(s);
            option::some::<str>(str::from_cstr(s))
        };
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn setenv(n: str, v: str) {
    // FIXME (868)
    str::as_buf(
        n,
        // FIXME (868)
        {|nbuf|
            str::as_buf(
                v,
                {|vbuf|
                    os::libc::setenv(nbuf, vbuf, 1i32)})});
}

#[cfg(target_os = "win32")]
fn getenv(n: str) -> option<str> {
    let nsize = 256u;
    while true {
        let v: [u8] = [];
        vec::reserve(v, nsize);
        let res =
            str::as_buf(n,
                        {|nbuf|
                            unsafe {
                            let vbuf = vec::to_ptr(v);
                            os::kernel32::GetEnvironmentVariableA(nbuf, vbuf,
                                                                  nsize)
                        }
                        });
        if res == 0u {
            ret option::none;
        } else if res < nsize {
            unsafe {
                vec::unsafe::set_len(v, res);
            }
            ret option::some(str::from_bytes(v)); // UTF-8 or fail
        } else { nsize = res; }
    }
    fail;
}

#[cfg(target_os = "win32")]
fn setenv(n: str, v: str) {
    // FIXME (868)
    let _: () =
        str::as_buf(n, {|nbuf|
            let _: () =
                str::as_buf(v, {|vbuf|
                    os::kernel32::SetEnvironmentVariableA(nbuf, vbuf);
                });
        });
}

#[cfg(test)]
mod tests {

    #[test]
    #[ignore(reason = "fails periodically on mac")]
    fn test_setenv() {
        // NB: Each test of setenv needs to use different variable names or
        // the tests will not be threadsafe
        setenv("NAME1", "VALUE");
        assert (getenv("NAME1") == option::some("VALUE"));
    }

    #[test]
    #[ignore(reason = "fails periodically on mac")]
    fn test_setenv_overwrite() {
        setenv("NAME2", "1");
        setenv("NAME2", "2");
        assert (getenv("NAME2") == option::some("2"));
    }

    // Windows GetEnvironmentVariable requires some extra work to make sure
    // the buffer the variable is copied into is the right size
    #[test]
    #[ignore(reason = "fails periodically on mac")]
    fn test_getenv_big() {
        let s = "";
        let i = 0;
        while i < 100 { s += "aaaaaaaaaa"; i += 1; }
        setenv("test_getenv_big", s);
        log(debug, s);
        assert (getenv("test_getenv_big") == option::some(s));
    }

    #[test]
    fn test_get_exe_path() {
        let path = os::get_exe_path();
        assert option::is_some(path);
        let path = option::get(path);
        log(debug, path);

        // Hard to test this function
        if os::target_os() != "win32" {
            assert str::starts_with(path, fs::path_sep());
        } else {
            assert path[1] == ':' as u8;
        }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>extern crate bindgen;
extern crate pkg_config;

use bindgen::RustTarget;
use pkg_config::Error;
use std::env;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {
            let bindings = bindgen::builder()
                .header("wrapper.h")
                .block_extern_crate(true)
                .opaque_type("fitsfile")
                .opaque_type("FITSfile")
                .rust_target(RustTarget::Stable_1_0)
                .generate()
                .expect("Unable to generate bindings");
            let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
            bindings
                .write_to_file(out_path.join("bindings.rs"))
                .expect("Couldn't write bindings");
        }
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}
<commit_msg>Follow clippy lint<commit_after>extern crate bindgen;
extern crate pkg_config;

use bindgen::RustTarget;
use pkg_config::Error;
use std::env;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {
            let bindings = bindgen::builder()
                .header("wrapper.h")
                .block_extern_crate(true)
                .opaque_type("fitsfile")
                .opaque_type("FITSfile")
                .rust_target(RustTarget::Stable_1_0)
                .generate()
                .expect("Unable to generate bindings");
            let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
            bindings
                .write_to_file(out_path.join("bindings.rs"))
                .expect("Couldn't write bindings");
        }
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write_all(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}
<|endoftext|>


<commit_before>use decoders::*;
use decoders::tiff::*;
use decoders::basics::*;
use std::mem::transmute;
use std::slice;
extern crate itertools;
use self::itertools::Itertools;
extern crate crossbeam;
use std::f32;
use std::cmp;

#[derive(Debug, Clone)]
pub struct ArwDecoder<'a> {
  buffer: &'a [u8],
  rawloader: &'a RawLoader,
  tiff: TiffIFD<'a>,
}

impl<'a> ArwDecoder<'a> {
  pub fn new(buf: &'a [u8], tiff: TiffIFD<'a>, rawloader: &'a RawLoader) -> ArwDecoder<'a> {
    ArwDecoder {
      buffer: buf,
      tiff: tiff,
      rawloader: rawloader,
    }
  }
}

impl<'a> Decoder for ArwDecoder<'a> {
  fn identify(&self) -> Result<&Camera, String> {
    let make = fetch_tag!(self.tiff, Tag::Make, "ARW: Couldn't find Make").get_str();
    let model = fetch_tag!(self.tiff, Tag::Model, "ARW: Couldn't find Model").get_str();
    self.rawloader.check_supported(make, model)
  }

  fn image(&self) -> Result<Image,String> {
    let camera = try!(self.identify());
    let data = self.tiff.find_ifds_with_tag(Tag::StripOffsets);
    if data.len() == 0 {
      return Err("ARW: Couldn't find the data IFD!".to_string())
    }
    let raw = data[0];
    let width = fetch_tag!(raw, Tag::ImageWidth, "ARW: Couldn't find width").get_u16(0) as u32;
    let height = fetch_tag!(raw, Tag::ImageLength, "ARW: Couldn't find height").get_u16(0) as u32;
    let offset = fetch_tag!(raw, Tag::StripOffsets, "ARW: Couldn't find offset").get_u32(0) as usize;
    let count = fetch_tag!(raw, Tag::StripByteCounts, "ARW: Couldn't find byte count").get_u32(0) as usize;
    let compression = fetch_tag!(raw, Tag::Compression, "ARW: Couldn't find Compression").get_u16(0);
    let bps = if camera.bps != 0 {
      camera.bps
    } else {
      fetch_tag!(raw, Tag::BitsPerSample, "ARW: Couldn't find bps").get_u16(0) as u32
    };
    let src = &self.buffer[offset .. self.buffer.len()];

    let image = match compression {
      1 => decode_16le(src, width as usize, height as usize),
      32767 => {
        if ((width*height*bps) as usize) != count*8 {
          ArwDecoder::decode_arw1(src, width, height)
        } else {
          match bps {
            8 => {let curve = try!(ArwDecoder::get_curve(raw)); ArwDecoder::decode_arw2(src, width, height, &curve)},
            12 => decode_12le(src, width as usize, height as usize),
            _ => return Err(format!("ARW2: Don't know how to decode images with {} bps", bps)),
          }
        }
      },
      _ => return Err(format!("ARW: Don't know how to decode type {}", compression).to_string()),
    };

    ok_image(camera, width, height, try!(self.get_wb()), image)
  }
}

impl<'a> ArwDecoder<'a> {
  fn decode_arw1(buf: &[u8], width: u32, height: u32) -> Vec<u16> {
    let mut buffer: Vec<u16> = vec![0; (width*height) as usize];

    buffer[0] = buf[0] as u16; // Shut up the warnings for now

    buffer
  }

  fn decode_arw2(buf: &[u8], width: u32, height: u32, curve: &LookupTable) -> Vec<u16> {
    let mut out: Vec<u16> = vec![0; (width*height) as usize];

    // Default to 4 threads as that should be close to the ideal speedup on most
    // machines. In the future we need a way to have this be a parameter or even
    // to set it dynamically based on how loaded the machine is.
    let threads = 4;
    if threads < 2 || height < threads {
      ArwDecoder::decode_arw2_slice(&mut out, buf, width, height, &curve);
      return out
    }

    {
      let mut heights = Vec::new();
      let split = height/threads;
      let mut height_split = 0;
      for i in 0..threads {
        let start = height_split;
        height_split += split;
        let end = if i == threads-1 { height } else { height_split -1 };
        let tall = end-start+1;
        let ptr = (&mut out[(start*width) as usize ..]).as_mut_ptr();
        let out_part = unsafe {
           slice::from_raw_parts_mut(ptr, (tall*width) as usize)
        };
        heights.push((start, tall, out_part));
      }

      crossbeam::scope(|scope| {
        let mut handles = Vec::new();
        for (start, tall, out_part) in heights {
          let src = &buf[((start*width) as usize)..buf.len()];
          let handle = scope.spawn(move || {
            ArwDecoder::decode_arw2_slice(out_part, src, width, tall, &curve)
          });
          handles.push(handle);
        }

        for h in handles { h.join() };
      });
    }
    out
  }

  fn decode_arw2_slice(out: &mut [u16], buf: &[u8], width: u32, height: u32, curve: &LookupTable) {
    let mut pump = BitPump::new(buf);

    for row in 0..height {
      // Process 16 pixels at a time in interleaved fashion
      let mut col = 0;
      let mut random = pump.peek_bits(24);
      while col < (width-30) {
        let max = pump.get_bits(11);
        let min = pump.get_bits(11);
        let imax = pump.get_bits(4);
        let imin = pump.get_bits(4);
        let mut sh = 0;
        while sh<4 && (0x80 << sh) <= (max - min) {sh = sh + 1;}
        for i in 0..16 {
          let val = if i == imax {
            max
          } else if i == imin {
            min
          } else {
            cmp::min(0x7ff,(pump.get_bits(7) << sh) + min)
          };
          out[(row*width+col+i*2) as usize] = curve.dither(val as u16, &mut random);
        }
        col += if (col & 1) != 0 {31} else {1};  // Skip to next 16 pixels
      }
    }
  }

  fn get_wb(&self) -> Result<[f32;4], String> {
    let priv_offset = fetch_tag!(self.tiff, Tag::DNGPrivateArea, "ARW: Couldn't find private offset").get_u32(0);
    let priv_tiff = TiffIFD::new(self.buffer, priv_offset as usize, 0, 0, LITTLE_ENDIAN);
    let sony_offset = fetch_tag!(priv_tiff, Tag::SonyOffset, "ARW: Couldn't find sony offset").get_u32(0) as usize;
    let sony_length = fetch_tag!(priv_tiff, Tag::SonyLength, "ARW: Couldn't find sony length").get_u32(0) as usize;
    let sony_key = fetch_tag!(priv_tiff, Tag::SonyKey, "ARW: Couldn't find sony key").get_u32(0);
    let decrypted_buf = ArwDecoder::sony_decrypt(self.buffer, sony_offset, sony_length, sony_key);
    let decrypted_tiff = TiffIFD::new(&decrypted_buf, 0, sony_offset, 0, LITTLE_ENDIAN);
    let grgb_levels = decrypted_tiff.find_entry(Tag::SonyGRBG);
    let rggb_levels = decrypted_tiff.find_entry(Tag::SonyRGGB);
    if grgb_levels.is_some() {
      let levels = grgb_levels.unwrap();
      Ok([levels.get_u16(1) as f32, levels.get_u16(0) as f32, levels.get_u16(2) as f32, f32::NAN])
    } else if rggb_levels.is_some() {
      let levels = rggb_levels.unwrap();
      Ok([levels.get_u16(0) as f32, levels.get_u16(1) as f32, levels.get_u16(3) as f32, f32::NAN])
    } else {
      Err("ARW: Couldn't find GRGB or RGGB levels".to_string())
    }
  }

  fn get_curve(raw: &TiffIFD) -> Result<LookupTable, String> {
    let centry = fetch_tag!(raw, Tag::SonyCurve, "ARW: Couldn't find sony curve");
    let mut curve: [u32;6] = [ 0, 0, 0, 0, 0, 4095 ];

    for i in 0..4 {
      curve[i+1] = ((centry.get_u16(i) >> 2) & 0xfff) as u32;
    }

    let mut out = vec![0 as u16; (curve[5]+1) as usize];
    for i in 0..5 {
      for j in (curve[i]+1)..(curve[i+1]+1) {
        out[j as usize] = out[(j-1) as usize] + (1<<i);
      }
    }

    Ok(LookupTable::new(&out))
  }

  fn sony_decrypt(buf: &[u8], offset: usize, length: usize, key: u32) -> Vec<u8>{
    let mut pad: [u32; 128] = [0 as u32; 128];
    let mut mkey = key;
    // Make sure we always have space for the final bytes even if the buffer
    // isn't a multiple of 4
    let mut out = vec![0;length+4];
    // Initialize the decryption pad from the key
    for p in 0..4 {
      mkey = mkey.wrapping_mul(48828125).wrapping_add(1);
      pad[p] = mkey;
    }
    pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31;
    for p in 4..127 {
      pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31;
    }
    for p in 0..127 {
      pad[p] = u32::from_be(pad[p]);
    }

    for i in (0..length).step(4) {
      let p = i/4 + 127;
      pad[p & 127] = pad[(p+1) & 127] ^ pad[(p+1+64) & 127];
      let output = LEu32(buf, offset+i) ^ pad[p & 127];
      let bytes: [u8; 4] = unsafe { transmute(output.to_le()) };
      out[i]   = bytes[0];
      out[i+1] = bytes[1];
      out[i+2] = bytes[2];
      out[i+3] = bytes[3];
    }
    out
  }
}
<commit_msg>ARW: simplify code and get rid of unsafe for ARW2<commit_after>use decoders::*;
use decoders::tiff::*;
use decoders::basics::*;
use std::mem::transmute;
extern crate itertools;
use self::itertools::Itertools;
extern crate crossbeam;
use std::f32;
use std::cmp;

#[derive(Debug, Clone)]
pub struct ArwDecoder<'a> {
  buffer: &'a [u8],
  rawloader: &'a RawLoader,
  tiff: TiffIFD<'a>,
}

impl<'a> ArwDecoder<'a> {
  pub fn new(buf: &'a [u8], tiff: TiffIFD<'a>, rawloader: &'a RawLoader) -> ArwDecoder<'a> {
    ArwDecoder {
      buffer: buf,
      tiff: tiff,
      rawloader: rawloader,
    }
  }
}

impl<'a> Decoder for ArwDecoder<'a> {
  fn identify(&self) -> Result<&Camera, String> {
    let make = fetch_tag!(self.tiff, Tag::Make, "ARW: Couldn't find Make").get_str();
    let model = fetch_tag!(self.tiff, Tag::Model, "ARW: Couldn't find Model").get_str();
    self.rawloader.check_supported(make, model)
  }

  fn image(&self) -> Result<Image,String> {
    let camera = try!(self.identify());
    let data = self.tiff.find_ifds_with_tag(Tag::StripOffsets);
    if data.len() == 0 {
      return Err("ARW: Couldn't find the data IFD!".to_string())
    }
    let raw = data[0];
    let width = fetch_tag!(raw, Tag::ImageWidth, "ARW: Couldn't find width").get_u16(0) as u32;
    let height = fetch_tag!(raw, Tag::ImageLength, "ARW: Couldn't find height").get_u16(0) as u32;
    let offset = fetch_tag!(raw, Tag::StripOffsets, "ARW: Couldn't find offset").get_u32(0) as usize;
    let count = fetch_tag!(raw, Tag::StripByteCounts, "ARW: Couldn't find byte count").get_u32(0) as usize;
    let compression = fetch_tag!(raw, Tag::Compression, "ARW: Couldn't find Compression").get_u16(0);
    let bps = if camera.bps != 0 {
      camera.bps
    } else {
      fetch_tag!(raw, Tag::BitsPerSample, "ARW: Couldn't find bps").get_u16(0) as u32
    };
    let src = &self.buffer[offset .. self.buffer.len()];

    let image = match compression {
      1 => decode_16le(src, width as usize, height as usize),
      32767 => {
        if ((width*height*bps) as usize) != count*8 {
          ArwDecoder::decode_arw1(src, width, height)
        } else {
          match bps {
            8 => {let curve = try!(ArwDecoder::get_curve(raw)); ArwDecoder::decode_arw2(src, width, height, &curve)},
            12 => decode_12le(src, width as usize, height as usize),
            _ => return Err(format!("ARW2: Don't know how to decode images with {} bps", bps)),
          }
        }
      },
      _ => return Err(format!("ARW: Don't know how to decode type {}", compression).to_string()),
    };

    ok_image(camera, width, height, try!(self.get_wb()), image)
  }
}

impl<'a> ArwDecoder<'a> {
  fn decode_arw1(buf: &[u8], width: u32, height: u32) -> Vec<u16> {
    let mut buffer: Vec<u16> = vec![0; (width*height) as usize];

    buffer[0] = buf[0] as u16; // Shut up the warnings for now

    buffer
  }

  fn decode_arw2(buf: &[u8], width: u32, height: u32, curve: &LookupTable) -> Vec<u16> {
    let mut out: Vec<u16> = vec![0; (width*height) as usize];

    // Default to 4 threads as that should be close to the ideal speedup on most
    // machines. In the future we need a way to have this be a parameter or even
    // to set it dynamically based on how loaded the machine is.
    let threads = 4;
    if threads < 2 || height < threads {
      ArwDecoder::decode_arw2_slice(&mut out, buf, width, height, &curve);
      return out
    }

    let mut split = height/threads;
    if split*threads < height { // Make sure the last split is always the smallest
      split += 1;
    }

    crossbeam::scope(|scope| {
      let mut handles = Vec::new();
      for (i,out_part) in (&mut out[..]).chunks_mut((split*width) as usize).enumerate() {
        let start = split*(i as u32);
        let tall = (out_part.len() as u32)/width;
        let src = &buf[((start*width) as usize)..buf.len()];
        let handle = scope.spawn(move || {
          ArwDecoder::decode_arw2_slice(out_part, src, width, tall, &curve)
        });
        handles.push(handle);
      }

      for h in handles { h.join() };
    });

    out
  }

  fn decode_arw2_slice(out: &mut [u16], buf: &[u8], width: u32, height: u32, curve: &LookupTable) {
    let mut pump = BitPump::new(buf);

    for row in 0..height {
      // Process 16 pixels at a time in interleaved fashion
      let mut col = 0;
      let mut random = pump.peek_bits(24);
      while col < (width-30) {
        let max = pump.get_bits(11);
        let min = pump.get_bits(11);
        let imax = pump.get_bits(4);
        let imin = pump.get_bits(4);
        let mut sh = 0;
        while sh<4 && (0x80 << sh) <= (max - min) {sh = sh + 1;}
        for i in 0..16 {
          let val = if i == imax {
            max
          } else if i == imin {
            min
          } else {
            cmp::min(0x7ff,(pump.get_bits(7) << sh) + min)
          };
          out[(row*width+col+i*2) as usize] = curve.dither(val as u16, &mut random);
        }
        col += if (col & 1) != 0 {31} else {1};  // Skip to next 16 pixels
      }
    }
  }

  fn get_wb(&self) -> Result<[f32;4], String> {
    let priv_offset = fetch_tag!(self.tiff, Tag::DNGPrivateArea, "ARW: Couldn't find private offset").get_u32(0);
    let priv_tiff = TiffIFD::new(self.buffer, priv_offset as usize, 0, 0, LITTLE_ENDIAN);
    let sony_offset = fetch_tag!(priv_tiff, Tag::SonyOffset, "ARW: Couldn't find sony offset").get_u32(0) as usize;
    let sony_length = fetch_tag!(priv_tiff, Tag::SonyLength, "ARW: Couldn't find sony length").get_u32(0) as usize;
    let sony_key = fetch_tag!(priv_tiff, Tag::SonyKey, "ARW: Couldn't find sony key").get_u32(0);
    let decrypted_buf = ArwDecoder::sony_decrypt(self.buffer, sony_offset, sony_length, sony_key);
    let decrypted_tiff = TiffIFD::new(&decrypted_buf, 0, sony_offset, 0, LITTLE_ENDIAN);
    let grgb_levels = decrypted_tiff.find_entry(Tag::SonyGRBG);
    let rggb_levels = decrypted_tiff.find_entry(Tag::SonyRGGB);
    if grgb_levels.is_some() {
      let levels = grgb_levels.unwrap();
      Ok([levels.get_u16(1) as f32, levels.get_u16(0) as f32, levels.get_u16(2) as f32, f32::NAN])
    } else if rggb_levels.is_some() {
      let levels = rggb_levels.unwrap();
      Ok([levels.get_u16(0) as f32, levels.get_u16(1) as f32, levels.get_u16(3) as f32, f32::NAN])
    } else {
      Err("ARW: Couldn't find GRGB or RGGB levels".to_string())
    }
  }

  fn get_curve(raw: &TiffIFD) -> Result<LookupTable, String> {
    let centry = fetch_tag!(raw, Tag::SonyCurve, "ARW: Couldn't find sony curve");
    let mut curve: [u32;6] = [ 0, 0, 0, 0, 0, 4095 ];

    for i in 0..4 {
      curve[i+1] = ((centry.get_u16(i) >> 2) & 0xfff) as u32;
    }

    let mut out = vec![0 as u16; (curve[5]+1) as usize];
    for i in 0..5 {
      for j in (curve[i]+1)..(curve[i+1]+1) {
        out[j as usize] = out[(j-1) as usize] + (1<<i);
      }
    }

    Ok(LookupTable::new(&out))
  }

  fn sony_decrypt(buf: &[u8], offset: usize, length: usize, key: u32) -> Vec<u8>{
    let mut pad: [u32; 128] = [0 as u32; 128];
    let mut mkey = key;
    // Make sure we always have space for the final bytes even if the buffer
    // isn't a multiple of 4
    let mut out = vec![0;length+4];
    // Initialize the decryption pad from the key
    for p in 0..4 {
      mkey = mkey.wrapping_mul(48828125).wrapping_add(1);
      pad[p] = mkey;
    }
    pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31;
    for p in 4..127 {
      pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31;
    }
    for p in 0..127 {
      pad[p] = u32::from_be(pad[p]);
    }

    for i in (0..length).step(4) {
      let p = i/4 + 127;
      pad[p & 127] = pad[(p+1) & 127] ^ pad[(p+1+64) & 127];
      let output = LEu32(buf, offset+i) ^ pad[p & 127];
      let bytes: [u8; 4] = unsafe { transmute(output.to_le()) };
      out[i]   = bytes[0];
      out[i+1] = bytes[1];
      out[i+2] = bytes[2];
      out[i+3] = bytes[3];
    }
    out
  }
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use Result;

use std::path::Path;

mod metadata_value;
use self::metadata_value::MetadataValue;

mod hook;
use self::hook::{run_hook, run_hooks_from_dir};

pub mod installation_set;

#[cfg(test)]
pub mod tests;

const PRODUCT_UID_HOOK: &str = "product-uid";
const VERSION_HOOK: &str = "version";
const HARDWARE_HOOK: &str = "hardware";
const DEVICE_IDENTITY_DIR: &str = "device-identity.d";
const DEVICE_ATTRIBUTES_DIR: &str = "device-attributes.d";

#[derive(Fail, Debug)]
pub enum Error {
    #[fail(display = "Invalid product UID")]
    InvalidProductUid,
    #[fail(display = "Product UID is missing")]
    MissingProductUid,
    #[fail(display = "Device Identity is missing")]
    MissingDeviceIdentity,
}

/// Metadata stores the firmware metadata information. It is
/// organized in multiple fields.
///
/// The Metadata is created loading its information from the running
/// firmware. It uses the `load` method for that.
#[derive(Debug, Serialize, PartialEq, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Metadata {
    /// Product UID which identifies the firmware on the management system
    pub product_uid: String,

    /// Version of firmware
    pub version: String,

    /// Hardware where the firmware is running
    pub hardware: String,

    /// Device Identity
    pub device_identity: MetadataValue,

    /// Device Attributes
    pub device_attributes: MetadataValue,
}

impl Metadata {
    pub fn new(path: &Path) -> Result<Self> {
        let product_uid_hook = path.join(PRODUCT_UID_HOOK);
        let version_hook = path.join(VERSION_HOOK);
        let hardware_hook = path.join(HARDWARE_HOOK);
        let device_identity_dir = path.join(DEVICE_IDENTITY_DIR);
        let device_attributes_dir = path.join(DEVICE_ATTRIBUTES_DIR);

        let metadata = Self {
            product_uid: run_hook(&product_uid_hook)?,
            version: run_hook(&version_hook)?,
            hardware: run_hook(&hardware_hook)?,
            device_identity: run_hooks_from_dir(&device_identity_dir)?,
            device_attributes: run_hooks_from_dir(&device_attributes_dir)?,
        };

        if metadata.product_uid.is_empty() {
            return Err(Error::MissingProductUid.into());
        }

        if metadata.product_uid.len() != 64 {
            return Err(Error::InvalidProductUid.into());
        }

        if metadata.device_identity.is_empty() {
            return Err(Error::MissingDeviceIdentity.into());
        }

        Ok(metadata)
    }
}
<commit_msg>firmware: Reduce imports on module root<commit_after>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use Result;

use std::path::Path;

use self::hook::{run_hook, run_hooks_from_dir};

mod hook;
mod metadata_value;

pub mod installation_set;

#[cfg(test)]
pub mod tests;

const PRODUCT_UID_HOOK: &str = "product-uid";
const VERSION_HOOK: &str = "version";
const HARDWARE_HOOK: &str = "hardware";
const DEVICE_IDENTITY_DIR: &str = "device-identity.d";
const DEVICE_ATTRIBUTES_DIR: &str = "device-attributes.d";

#[derive(Fail, Debug)]
pub enum Error {
    #[fail(display = "Invalid product UID")]
    InvalidProductUid,
    #[fail(display = "Product UID is missing")]
    MissingProductUid,
    #[fail(display = "Device Identity is missing")]
    MissingDeviceIdentity,
}

/// Metadata stores the firmware metadata information. It is
/// organized in multiple fields.
///
/// The Metadata is created loading its information from the running
/// firmware. It uses the `load` method for that.
#[derive(Debug, Serialize, PartialEq, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Metadata {
    /// Product UID which identifies the firmware on the management system
    pub product_uid: String,

    /// Version of firmware
    pub version: String,

    /// Hardware where the firmware is running
    pub hardware: String,

    /// Device Identity
    pub device_identity: metadata_value::MetadataValue,

    /// Device Attributes
    pub device_attributes: metadata_value::MetadataValue,
}

impl Metadata {
    pub fn new(path: &Path) -> Result<Self> {
        let product_uid_hook = path.join(PRODUCT_UID_HOOK);
        let version_hook = path.join(VERSION_HOOK);
        let hardware_hook = path.join(HARDWARE_HOOK);
        let device_identity_dir = path.join(DEVICE_IDENTITY_DIR);
        let device_attributes_dir = path.join(DEVICE_ATTRIBUTES_DIR);

        let metadata = Self {
            product_uid: run_hook(&product_uid_hook)?,
            version: run_hook(&version_hook)?,
            hardware: run_hook(&hardware_hook)?,
            device_identity: run_hooks_from_dir(&device_identity_dir)?,
            device_attributes: run_hooks_from_dir(&device_attributes_dir)?,
        };

        if metadata.product_uid.is_empty() {
            return Err(Error::MissingProductUid.into());
        }

        if metadata.product_uid.len() != 64 {
            return Err(Error::InvalidProductUid.into());
        }

        if metadata.device_identity.is_empty() {
            return Err(Error::MissingDeviceIdentity.into());
        }

        Ok(metadata)
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::{io, os, str};

use test::{black_box, BenchHarness, TestDesc, TestDescAndFn};
use test::{DynTestName, DynBenchFn, TDynBenchFn};

use html5::tokenizer::{TokenSink, Token, Tokenizer, TokenizerOpts};

struct Sink;

impl TokenSink for Sink {
    fn process_token(&mut self, token: Token) {
        // Don't use the token, but make sure we don't get
        // optimized out entirely.
        black_box(token);
    }
}

// This could almost be the TokenSink too, but it's not
// mut within run().
struct Bench {
    input: ~str,
    clone_only: bool,
    opts: TokenizerOpts,
}

impl Bench {
    fn new(name: &str, size: Option<uint>, clone_only: bool,
           opts: TokenizerOpts) -> Bench {
        let mut path = os::self_exe_path().expect("can't get exe path");
        path.push("../data/bench/");
        path.push(name);
        let mut file = io::File::open(&path).ok().expect("can't open file");
        let file_input = file.read_to_str().ok().expect("can't read file");

        let input = match size {
            None => file_input,
            Some(size) => {
                // Replicate the input in memory up to the desired size.
                let mut input = str::with_capacity(size);
                while input.len() < size {
                    input.push_str(file_input);
                }
                input
            }
        };

        Bench {
            input: input,
            clone_only: clone_only,
            opts: opts,
        }
    }
}

impl TDynBenchFn for Bench {
    fn run(&self, bh: &mut BenchHarness) {
        bh.iter(|| {
            let input = self.input.clone();
            if self.clone_only {
                // Because the tokenizer consumes its buffers, we need
                // to clone inside iter().  We can benchmark this
                // separately and subtract it out.
                black_box(input);
            } else {
                let mut sink = Sink;
                let mut tok = Tokenizer::new(&mut sink, self.opts.clone());
                tok.feed(self.input.clone());
                tok.end();
            }
        });
    }
}

fn make_bench(name: &str, size: Option<uint>, clone_only: bool,
              opts: TokenizerOpts) -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName([
                ~"tokenize ",
                name.to_owned(),
                size.map_or(~"", |s| format!(" size {:7u}", s)),
                if clone_only { ~" (clone only)" } else { ~"" },
                if opts.exact_errors { ~" (exact errors)" } else { ~"" },
            ].concat()),
            ignore: false,
            should_fail: false,
        },
        testfn: DynBenchFn(~Bench::new(name, size, clone_only, opts)),
    }
}

pub fn tests() -> ~[TestDescAndFn] {
    let opts_exact = TokenizerOpts {
        exact_errors: true,
        .. Default::default()
    };
    let mut tests = ~[
        make_bench("lipsum.html", Some(1024*1024), true, Default::default()),
    ];

    for opts in [Default::default(), opts_exact].iter() {
        for &file in ["lipsum.html", "lipsum-zh.html", "strong.html"].iter() {
            for &sz in [1024, 1024*1024].iter() {
                tests.push(make_bench(file, Some(sz), false, opts.clone()));
            }
        }

        if os::getenv("BENCH_UNCOMMITTED").is_some() {
            // Not checked into the repo, so don't include by default.
            for &file in ["webapps.html", "sina.com.cn.html", "wikipedia.html"].iter() {
                let name: ~str = (~"uncommitted/").append(file);
                tests.push(make_bench(name.as_slice(), None, false, opts.clone()));
            }
        }
    }

    tests
}
<commit_msg>Make exact-errors benchmarks optional<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::{io, os, str};

use test::{black_box, BenchHarness, TestDesc, TestDescAndFn};
use test::{DynTestName, DynBenchFn, TDynBenchFn};

use html5::tokenizer::{TokenSink, Token, Tokenizer, TokenizerOpts};

struct Sink;

impl TokenSink for Sink {
    fn process_token(&mut self, token: Token) {
        // Don't use the token, but make sure we don't get
        // optimized out entirely.
        black_box(token);
    }
}

// This could almost be the TokenSink too, but it's not
// mut within run().
struct Bench {
    input: ~str,
    clone_only: bool,
    opts: TokenizerOpts,
}

impl Bench {
    fn new(name: &str, size: Option<uint>, clone_only: bool,
           opts: TokenizerOpts) -> Bench {
        let mut path = os::self_exe_path().expect("can't get exe path");
        path.push("../data/bench/");
        path.push(name);
        let mut file = io::File::open(&path).ok().expect("can't open file");
        let file_input = file.read_to_str().ok().expect("can't read file");

        let input = match size {
            None => file_input,
            Some(size) => {
                // Replicate the input in memory up to the desired size.
                let mut input = str::with_capacity(size);
                while input.len() < size {
                    input.push_str(file_input);
                }
                input
            }
        };

        Bench {
            input: input,
            clone_only: clone_only,
            opts: opts,
        }
    }
}

impl TDynBenchFn for Bench {
    fn run(&self, bh: &mut BenchHarness) {
        bh.iter(|| {
            let input = self.input.clone();
            if self.clone_only {
                // Because the tokenizer consumes its buffers, we need
                // to clone inside iter().  We can benchmark this
                // separately and subtract it out.
                black_box(input);
            } else {
                let mut sink = Sink;
                let mut tok = Tokenizer::new(&mut sink, self.opts.clone());
                tok.feed(self.input.clone());
                tok.end();
            }
        });
    }
}

fn make_bench(name: &str, size: Option<uint>, clone_only: bool,
              opts: TokenizerOpts) -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName([
                ~"tokenize ",
                name.to_owned(),
                size.map_or(~"", |s| format!(" size {:7u}", s)),
                if clone_only { ~" (clone only)" } else { ~"" },
                if opts.exact_errors { ~" (exact errors)" } else { ~"" },
            ].concat()),
            ignore: false,
            should_fail: false,
        },
        testfn: DynBenchFn(~Bench::new(name, size, clone_only, opts)),
    }
}

pub fn tests() -> ~[TestDescAndFn] {
    let mut tests = ~[
        make_bench("lipsum.html", Some(1024*1024), true, Default::default()),
    ];

    let mut opts_vec = ~[Default::default()];
    if os::getenv("BENCH_EXACT_ERRORS").is_some() {
        opts_vec.push(TokenizerOpts {
            exact_errors: true,
            .. Default::default()
        });
    }

    for opts in opts_vec.iter() {
        for &file in ["lipsum.html", "lipsum-zh.html", "strong.html"].iter() {
            for &sz in [1024, 1024*1024].iter() {
                tests.push(make_bench(file, Some(sz), false, opts.clone()));
            }
        }

        if os::getenv("BENCH_UNCOMMITTED").is_some() {
            // Not checked into the repo, so don't include by default.
            for &file in ["webapps.html", "sina.com.cn.html", "wikipedia.html"].iter() {
                let name: ~str = (~"uncommitted/").append(file);
                tests.push(make_bench(name.as_slice(), None, false, opts.clone()));
            }
        }
    }

    tests
}
<|endoftext|>