[vsipl++] Matlab IO

Assem Salama assem at codesourcery.com
Wed Jun 28 03:18:41 UTC 2006


Jules Bergmann wrote:
> Assem Salama wrote:
>> Assem Salama wrote:
>>> Everyone,
>>>  Fixed endian stuff.
>
> Assem,
>
>>> +
>>> +  template <typename T,size_t type_size,bool to_swap_or_not_to_swap>
>>> +  struct swap_value +  { +    static void swap(T *d) {d=d;} +  };
>
> For our coding convention, struct and class names should begin with a 
> capital letter: 'Swap_value'
>
>>> +
>>> +  template <typename T>
>>> +  struct swap_value<T,2,true>
>>> +  {
>>> +    static void swap(T* d)
>>> +    {
>>> +      char *p = reinterpret_cast<char*>(d);
>>> +      std::swap(p[0],p[1]);
>>> +    }
>>> +  };
>>> +
>>> +  template <typename T>
>>> +  struct swap_value<T,4,true>
>>> +  {
>>> +    static void swap(T* d)
>>> +    {
>>> +      char *p = reinterpret_cast<char*>(d);
>>> +      std::swap(p[0],p[3]);
>>> +      std::swap(p[1],p[2]);
>>> +    }
>>> +  };
>>> +
>>> +  template <typename T>
>>> +  struct swap_value<T,8,true>
>>> +  {
>>> +    static void swap(T* d)
>>> +    {
>>> +      char *p = reinterpret_cast<char*>(d);
>>> +      std::swap(p[0],p[7]);
>>> +      std::swap(p[1],p[6]);
>>> +      std::swap(p[2],p[5]);
>>> +      std::swap(p[3],p[4]);
>>> +    }
>>> +  };
>
> You can wrap the 'Swap_value' class with a function:
>
> template <typename T>
> void
> swap_value(T& value, bool swap)
> {
>   if (swap)
>     swap_value<T, sizeof(T), true>::swap(&value);
> }
>
The reason that I didn't want this to be a function is because if I call 
this in a loop, it will execute a condition every iteration of the loop. 
If it is a template parameter, it might be a little better. What do you 
think?
>>> +
>>> +  // swaps an array of values based on a template param
>>> +  template <typename T>
>>> +  void swap_array(T *d, vsip::impl::Int_type<1>)
>>> +  { swap_value<T,sizeof(T),true>::swap(&(d[0])); }
>>> +  template <typename T>
>>> +  void swap_array(T *d, vsip::impl::Int_type<2>)
>>> +  { swap_value<T,sizeof(T),true>::swap(&(d[0]));
>>> +    swap_value<T,sizeof(T),true>::swap(&(d[1])); }
>>> +  template <typename T>
>>> +  void swap_array(T *d, vsip::impl::Int_type<3>)
>>> +  { swap_value<T,sizeof(T),true>::swap(&(d[0]));
>>> +    swap_value<T,sizeof(T),true>::swap(&(d[1]));
>>> +    swap_value<T,sizeof(T),true>::swap(&(d[2])); }
>
> Is there a reason that swap_array takes the array size as a template 
> parameter?
yes, because I call this function when Dim is a template parameter and I 
cannot iterate over this number.
>
> The pros of passing the size this way are primarily in performance.  
> The above code will force the compiler to inline the individual calls 
> instead of performing a loop.
>
> However, the cons are increased code size (both source code and object 
> code), longer compile times, and limiting the situations where the 
> function can be called.
>
> Can you change swap_array to take the array size as a regular parameter.
>
>>> +
>>> +  // swaps the header of a view
>>> +  template <vsip::dimension_type dim>
>>> +  void swap_header(view_header<dim> &header, uint16_t endian)
>
> Instead of passing 'endian' to each function and having it decode 'MI' 
> vs 'IM' to determine if swapping is necessary, it would be better to 
> have operator>> perform the check and pass a boolean parameter:
>
>     void swap_header(..., bool swap_bytes)
>
> This centralizes the knowledge of how matlab files encode endianness 
> into one place in the code instead of replicating it.  It also makes 
> the function's behavior easier to understand.
Sounds much better.
>
>>> +  {
>
> With the 'swap_value' helper function and the 'swap_bytes' bool 
> parameter, these become:
>
>     swap_value(header.header.type, swap_bytes);
>     swap_value(header.header.size, swap_bytes);
>     ...
>
>>> +    if(endian == ('I' << 8 | 'M') )
>>> +    {
>>> +      // swap all fields
>>> +      swap_value<int32_t,4,true>::swap(&(header.header.type));
>>> +      swap_value<uint32_t,4,true>::swap(&(header.header.size));
>>> +      
>>> swap_value<int32_t,4,true>::swap(&(header.array_flags_header.type));
>>> +      
>>> swap_value<uint32_t,4,true>::swap(&(header.array_flags_header.size));
>>> +      swap_value<int32_t,4,true>::swap(&(header.dim_header.type));
>>> +      swap_value<uint32_t,4,true>::swap(&(header.dim_header.size));
>>> +      
>>> swap_value<int32_t,4,true>::swap(&(header.array_name_header.type));
>>> +      
>>> swap_value<uint32_t,4,true>::swap(&(header.array_name_header.size));
>>> +      swap_array<uint32_t>(header.dim, vsip::impl::Int_type<dim>());
>>> +      swap_array<uint32_t>(header.array_flags, 
>>> vsip::impl::Int_type<2>());
>>> +    }
>>> +  }
>>> +
>>> +  // generic reader that allows us to read a generic type and cast 
>>> to another
>>> +  +  // the read function for real or complex depending of the view 
>>> that was
>>> +  // passed in
>
> What does "generic type" mean in the above comment?
>
> A comment like the following would be more clear:
>
> // Read a N-dimensional matlab XXX.  The value type of the file object
> // is specified by T1 and can be different from the value type of the
> // VSIPL++ view.  For views of complex data, this function is called
> // once for the real subview and once for the imaginary subview.
>
>>> +  template <typename T1,
>>> +        typename ViewT>
>>> +  void read(std::istream& is,ViewT v,uint16_t endian)
>>> +  {
>>> +    vsip::dimension_type const View_dim = ViewT::dim;
>>> +    vsip::Index<View_dim> my_index;
>>> +    vsip::impl::Length<View_dim> v_extent = extent(v);
>>> +    T1 data;
>>> +    typedef typename ViewT::value_type scalar_type;
>>> +    typedef void (*fn_type)(T1 *data);
>>> +    fn_type swap_fn;
>>> +
>>> +    // get num_points
>>> +    vsip::length_type num_points = v.size();
>>> +
>>> +    // figure out if we need to do endian swaps
>>> +    if(endian != ('M' << 8 | 'I'))
>>> +      swap_fn = swap_value<T1,sizeof(T1),true>::swap;
>>> +    else
>>> +      swap_fn = swap_value<T1,sizeof(T1),false>::swap;
>>> +
>>> +    // read all the points
>>> +    for(vsip::index_type i=0;i<num_points;i++) {
>>> +      is.read(reinterpret_cast<char*>(&data),sizeof(data));
>>> +      swap_fn(&data);
>
> With 'swap_value' wrapper, this becomes:
>
>       swap_value(data, swap_bytes);

>
>>> +      put(v,my_index,scalar_type(data));
>>> +
>>> +      // increment index
>>> +      my_index = vsip::impl::next(v_extent,my_index);
>>> +    }
>>> +
>>> +  }
>>> +
>
>
>>> +
>>> +// operator to read view from matlab file
>>> +template <typename T,
>>> +          typename Block0,
>>> +      template <typename,typename> class View>
>>> +inline
>>> +std::istream&
>>> +operator>>(
>>> +  std::istream&                                       is,
>>> +  Matlab_bin_formatter<View<T,Block0> >               mbf)
>>> +{
>>> +  matlab::data_element temp_data_element;
>>> +  matlab::view_header<vsip::impl::Dim_of_view<View>::dim> m_view;
>>> +  typedef typename vsip::impl::Scalar_of<T>::type scalar_type;
>>> +  typedef matlab::Subview_helper<View<T,Block0> > subview;
>>> +  typedef typename subview::realview_type r_v;
>>> +  typedef typename subview::imagview_type i_v;
>>> +  vsip::dimension_type v_dim = vsip::impl::Dim_of_view<View>::dim;
>>> +
>>> +
>>> +  // read header
>>> +  is.read(reinterpret_cast<char*>(&m_view),sizeof(m_view));
>
> After reading the header, this is the place to check 'endian' to 
> determine if byte swapping is necessary:
>
>     bool swap_bytes = (endian != 'M' << 8 | 'I');
>
>>> +
>>> +  // do we need to swap fields?
>
> Since we already know if it is necessary to swap bytes, the comment 
> might be:
>
>     // Swap header fields if necessary.
>
>>> +  matlab::swap_header(m_view,mbf.header.endian);
>>> +
>>> +  // is this complex?
>>> +  if(vsip::impl::Is_complex<T>::value && 
>>> !(m_view.array_flags[0]&(1<<11)))
>>> +    VSIP_IMPL_THROW(std::runtime_error(
>>> +      "Trying to read complex matrix into a real matrix"));
>>> +
>>> +
>>> +  // is this the same class?
>>> +  if(!((m_view.array_flags[0] & 0xff) == +            
>>> (matlab::Matlab_header_traits<sizeof(scalar_type),
>>> +                  std::numeric_limits<scalar_type>::is_signed,
>>> +                  
>>> std::numeric_limits<scalar_type>::is_integer>::class_type)
>>> +        ))
>>> +    VSIP_IMPL_THROW(std::runtime_error(
>>> +      "Trying to read a matrix of a different class"));
>
> The error message should mention "view" instead of "matrix":
>
>     "Trying to read a view of a different class"
>
>>> +
>>> +  // do dimensions agree?
>>> +  if(v_dim == 1) m_view.dim_header.size -= 4; // special case for 
>>> vectors
>>> +  if(v_dim != (m_view.dim_header.size/4))
>>> +    VSIP_IMPL_THROW(std::runtime_error(
>>> +      "Trying to read a matrix of different dimensions"));
>
>     "Trying to read a view with different dimensions"
>
>>> +
>>> +  for(vsip::dimension_type i=0;i<v_dim;i++)
>>> +    if(mbf.view.size(i) != m_view.dim[i])
>>> +      VSIP_IMPL_THROW(std::runtime_error(
>>> +        "Matrix dimensions don't agree"));
>
>     "View dimensions don't agree"
>
>
>




More information about the vsipl++ mailing list