Logo ROOT   6.12/07
Reference Guide
rhysd_array_view.hxx
Go to the documentation of this file.
1 /// \file ROOT/rhysd_array_view.h
2 /// \ingroup Base StdExt
3 /// \author Axel Naumann <axel@cern.ch>
4 /// \date 2015-09-06
5 
6 /*************************************************************************
7  * Copyright (C) 1995-2015, Rene Brun and Fons Rademakers. *
8  * All rights reserved. *
9  * *
10  * For the licensing terms see $ROOTSYS/LICENSE. *
11  * For the list of contributors see $ROOTSYS/README/CREDITS. *
12  *************************************************************************/
13 
14 #ifndef ROOT_RHYSD_ARRAY_VIEW_H
15 #define ROOT_RHYSD_ARRAY_VIEW_H
16 
17 // Necessary to compile in c++11 mode
18 #if __cplusplus >= 201402L
19 #define R__CONSTEXPR_IF_CXX14 constexpr
20 #else
21 #define R__CONSTEXPR_IF_CXX14
22 #endif
23 
24 // From https://github.com/rhysd/array_view/blob/master/include/array_view.hpp
25 
26 #include <cstddef>
27 #include <iterator>
28 #include <array>
29 #include <vector>
30 #include <stdexcept>
31 #include <memory>
32 #include <type_traits>
33 #include <vector>
34 #include <initializer_list>
35 
36 namespace std {
37 
38 inline namespace __ROOT {
39 
40 using std::size_t;
41 
42 namespace detail {
43 // detail meta functions {{{
44 template<class Array>
46  static bool const value = false;
47 };
48 template<class T, size_t N>
49 struct is_array_class<std::array<T, N>> {
50  static bool const value = true;
51 };
52 template<class T>
53 struct is_array_class<std::vector<T>> {
54  static bool const value = true;
55 };
56 template<class T>
57 struct is_array_class<std::initializer_list<T>> {
58  static bool const value = true;
59 };
60 // }}}
61 
62 // index sequences {{{
63 template< size_t... Indices >
64 struct indices{
65  static constexpr size_t value[sizeof...(Indices)] = {Indices...};
66 };
67 
68 template<class IndicesType, size_t Next>
70 
71 template<size_t... Indices, size_t Next>
72 struct make_indices_next<indices<Indices...>, Next> {
73  typedef indices<Indices..., (Indices + Next)...> type;
74 };
75 
76 template<class IndicesType, size_t Next, size_t Tail>
78 
79 template<size_t... Indices, size_t Next, size_t Tail>
80 struct make_indices_next2<indices<Indices...>, Next, Tail> {
81  typedef indices<Indices..., (Indices + Next)..., Tail> type;
82 };
83 
84 template<size_t First, size_t Step, size_t N, class = void>
86 
87 template<size_t First, size_t Step, size_t N>
89  First,
90  Step,
91  N,
92  typename std::enable_if<(N == 0)>::type
93 > {
94  typedef indices<> type;
95 };
96 
97 template<size_t First, size_t Step, size_t N>
99  First,
100  Step,
101  N,
102  typename std::enable_if<(N == 1)>::type
103 > {
105 };
106 
107 template<size_t First, size_t Step, size_t N>
109  First,
110  Step,
111  N,
112  typename std::enable_if<(N > 1 && N % 2 == 0)>::type
113 >
115  typename detail::make_indices_impl<First, Step, N / 2>::type,
116  First + N / 2 * Step
117  >
118 {};
119 
120 template<size_t First, size_t Step, size_t N>
121 struct make_indices_impl<
122  First,
123  Step,
124  N,
125  typename std::enable_if<(N > 1 && N % 2 == 1)>::type
126 >
128  typename detail::make_indices_impl<First, Step, N / 2>::type,
129  First + N / 2 * Step,
130  First + (N - 1) * Step
131  >
132 {};
133 
134 template<size_t First, size_t Last, size_t Step = 1>
137  First,
138  Step,
139  ((Last - First) + (Step - 1)) / Step
140  >
141 {};
142 
143 template < size_t Start, size_t Last, size_t Step = 1 >
145 // }}}
146 } // namespace detail
147 
148 // array_view {{{
149 
150 struct check_bound_t {};
151 static constexpr check_bound_t check_bound{};
152 
153 template<class T>
154 class array_view {
155 public:
156  /*
157  * types
158  */
159  typedef T value_type;
160  typedef value_type const* pointer;
161  typedef value_type const* const_pointer;
162  typedef value_type const& reference;
163  typedef value_type const& const_reference;
164  typedef value_type const* iterator;
165  typedef value_type const* const_iterator;
166  typedef size_t size_type;
167  typedef ptrdiff_t difference_type;
168  typedef std::reverse_iterator<iterator> reverse_iterator;
169  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
170 
171  /*
172  * ctors and assign operators
173  */
174  constexpr array_view() noexcept
175  : length_(0), data_(nullptr)
176  {}
177 
178  constexpr array_view(array_view const&) noexcept = default;
179  constexpr array_view(array_view &&) noexcept = default;
180 
181  // Note:
182  // This constructor can't be constexpr because & operator can't be constexpr.
183  template<size_type N>
184  /*implicit*/ array_view(std::array<T, N> const& a) noexcept
185  : length_(N), data_(N > 0 ? a.data() : nullptr)
186  {}
187 
188  // Note:
189  // This constructor can't be constexpr because & operator can't be constexpr.
190  template<size_type N>
191  /*implicit*/ array_view(T const (& a)[N]) noexcept
192  : length_(N), data_(N > 0 ? std::addressof(a[0]) : nullptr)
193  {
194  static_assert(N > 0, "Zero-length array is not permitted in ISO C++.");
195  }
196 
197  /*implicit*/ array_view(std::vector<T> const& v) noexcept
198  : length_(v.size()), data_(v.empty() ? nullptr : v.data())
199  {}
200 
201  /*implicit*/ constexpr array_view(T const* a, size_type const n) noexcept
202  : length_(n), data_(a)
203  {}
204 
205  template<
206  class InputIterator,
207  class = typename std::enable_if<
208  std::is_same<
209  T,
210  typename std::iterator_traits<InputIterator>::value_type
211  >::value
212  >::type
213  >
214  explicit array_view(InputIterator start, InputIterator last)
215  : length_(std::distance(start, last)), data_(start)
216  {}
217 
218  array_view(std::initializer_list<T> const& l)
219  : length_(l.size()), data_(std::begin(l))
220  {}
221 
222  array_view& operator=(array_view const&) noexcept = delete;
223  array_view& operator=(array_view &&) noexcept = delete;
224 
225  /*
226  * iterator interfaces
227  */
228  constexpr const_iterator begin() const noexcept
229  {
230  return data_;
231  }
232  constexpr const_iterator end() const noexcept
233  {
234  return data_ + length_;
235  }
236  constexpr const_iterator cbegin() const noexcept
237  {
238  return begin();
239  }
240  constexpr const_iterator cend() const noexcept
241  {
242  return end();
243  }
244  const_reverse_iterator rbegin() const
245  {
246  return {end()};
247  }
248  const_reverse_iterator rend() const
249  {
250  return {begin()};
251  }
252  const_reverse_iterator crbegin() const
253  {
254  return rbegin();
255  }
256  const_reverse_iterator crend() const
257  {
258  return rend();
259  }
260 
261  /*
262  * access
263  */
264  constexpr size_type size() const noexcept
265  {
266  return length_;
267  }
268  constexpr size_type length() const noexcept
269  {
270  return size();
271  }
272  constexpr size_type max_size() const noexcept
273  {
274  return size();
275  }
276  constexpr bool empty() const noexcept
277  {
278  return length_ == 0;
279  }
280  constexpr const_reference operator[](size_type const n) const noexcept
281  {
282  return *(data_ + n);
283  }
284  constexpr const_reference at(size_type const n) const
285  {
286  //Works only in C++14
287  //if (n >= length_) throw std::out_of_range("array_view::at()");
288  //return *(data_ + n);
289  return n >= length_ ? throw std::out_of_range("array_view::at()") : *(data_ + n);
290  }
291  constexpr const_pointer data() const noexcept
292  {
293  return data_;
294  }
295  constexpr const_reference front() const noexcept
296  {
297  return *data_;
298  }
299  constexpr const_reference back() const noexcept
300  {
301  return *(data_ + length_ - 1);
302  }
303 
304  /*
305  * slices
306  */
307  // slice with indices {{{
308  // check bound {{{
309  constexpr array_view<T> slice(check_bound_t, size_type const pos, size_type const slicelen) const
310  {
311  //Works only in C++14
312  //if (pos >= length_ || pos + slicelen >= length_) {
313  // throw std::out_of_range("array_view::slice()");
314  //}
315  //return array_view<T>{begin() + pos, begin() + pos + slicelen};
316  return pos >= length_ || pos + slicelen >= length_ ? throw std::out_of_range("array_view::slice()") : array_view<T>{begin() + pos, begin() + pos + slicelen};
317  }
318  constexpr array_view<T> slice_before(check_bound_t, size_type const pos) const
319  {
320  //Works only in C++14
321  //if (pos >= length_) {
322  // throw std::out_of_range("array_view::slice()");
323  //}
324  //return array_view<T>{begin(), begin() + pos};
325  return pos >= length_ ? std::out_of_range("array_view::slice()") : array_view<T>{begin(), begin() + pos};
326  }
327  constexpr array_view<T> slice_after(check_bound_t, size_type const pos) const
328  {
329  //Works only in C++14
330  //if (pos >= length_) {
331  // throw std::out_of_range("array_view::slice()");
332  //}
333  //return array_view<T>{begin() + pos, end()};
334  return pos >= length_ ? std::out_of_range("array_view::slice()") : array_view<T>{begin() + pos, end()};
335  }
336  // }}}
337  // not check bound {{{
338  constexpr array_view<T> slice(size_type const pos, size_type const slicelen) const
339  {
340  return array_view<T>{begin() + pos, begin() + pos + slicelen};
341  }
342  constexpr array_view<T> slice_before(size_type const pos) const
343  {
344  return array_view<T>{begin(), begin() + pos};
345  }
346  constexpr array_view<T> slice_after(size_type const pos) const
347  {
348  return array_view<T>{begin() + pos, end()};
349  }
350  // }}}
351  // }}}
352  // slice with iterators {{{
353  // check bound {{{
354  constexpr array_view<T> slice(check_bound_t, iterator start, iterator last) const
355  {
356  //Works only in C++14
357  //if ( start >= end() ||
358  // last > end() ||
359  // start > last ||
360  // static_cast<size_t>(std::distance(start, last > end() ? end() : last)) > length_ - std::distance(begin(), start) ) {
361  // throw std::out_of_range("array_view::slice()");
362  //}
363  //return array_view<T>{start, last > end() ? end() : last};
364  return ( start >= end() ||
365  last > end() ||
366  start > last ||
367  static_cast<size_t>(std::distance(start, last > end() ? end() : last)) > length_ - std::distance(begin(), start) ) ? throw std::out_of_range("array_view::slice()") : array_view<T>{start, last > end() ? end() : last};
368  }
369  constexpr array_view<T> slice_before(check_bound_t, iterator const pos) const
370  {
371  //Works only in C++14
372  //if (pos < begin() || pos > end()) {
373  // throw std::out_of_range("array_view::slice()");
374  //}
375  //return array_view<T>{begin(), pos > end() ? end() : pos};
376  return pos < begin() || pos > end() ? throw std::out_of_range("array_view::slice()") : array_view<T>{begin(), pos > end() ? end() : pos};
377  }
378  constexpr array_view<T> slice_after(check_bound_t, iterator const pos) const
379  {
380  //Works only in C++14
381  // if (pos < begin() || pos > end()) {
382  // throw std::out_of_range("array_view::slice()");
383  //}
384  //return array_view<T>{pos < begin() ? begin() : pos, end()};
385  return pos < begin() || pos > end() ? throw std::out_of_range("array_view::slice()") : array_view<T>{pos < begin() ? begin() : pos, end()};
386  }
387  // }}}
388  // not check bound {{{
389  constexpr array_view<T> slice(iterator start, iterator last) const
390  {
391  return array_view<T>{start, last};
392  }
393  constexpr array_view<T> slice_before(iterator const pos) const
394  {
395  return array_view<T>{begin(), pos};
396  }
397  constexpr array_view<T> slice_after(iterator const pos) const
398  {
399  return array_view<T>{pos, end()};
400  }
401  // }}}
402  // }}}
403 
404  /*
405  * others
406  */
407  template<class Allocator = std::allocator<T>>
408  auto to_vector(Allocator const& alloc = Allocator{}) const
409  -> std::vector<T, Allocator>
410  {
411  return {begin(), end(), alloc};
412  }
413 
414  template<size_t N>
415  auto to_array() const
416  -> std::array<T, N>
417  {
418  return to_array_impl(detail::make_indices<0, N>{});
419  }
420 private:
421  template<size_t... I>
423  -> std::array<T, sizeof...(I)>
424  {
425  return {{(I < length_ ? *(data_ + I) : T{} )...}};
426  }
427 
428 private:
429  size_type const length_;
430  const_pointer const data_;
431 };
432 // }}}
433 
434 // compare operators {{{
435 namespace detail {
436 
437 template< class ArrayL, class ArrayR >
439 bool operator_equal_impl(ArrayL const& lhs, size_t const lhs_size, ArrayR const& rhs, size_t const rhs_size)
440 {
441  if (lhs_size != rhs_size) {
442  return false;
443  }
444 
445  auto litr = std::begin(lhs);
446  auto ritr = std::begin(rhs);
447  for (; litr != std::end(lhs); ++litr, ++ritr) {
448  if (!(*litr == *ritr)) {
449  return false;
450  }
451  }
452 
453  return true;
454 }
455 } // namespace detail
456 
457 template<class T1, class T2>
458 inline constexpr
459 bool operator==(array_view<T1> const& lhs, array_view<T2> const& rhs)
460 {
461  return detail::operator_equal_impl(lhs, lhs.length(), rhs, rhs.length());
462 }
463 
464 template<
465  class T,
466  class Array,
467  class = typename std::enable_if<
469  >::type
470 >
471 inline constexpr
472 bool operator==(array_view<T> const& lhs, Array const& rhs)
473 {
474  return detail::operator_equal_impl(lhs, lhs.length(), rhs, rhs.size());
475 }
476 
477 template<class T1, class T2, size_t N>
478 inline constexpr
479 bool operator==(array_view<T1> const& lhs, T2 const (& rhs)[N])
480 {
481  return detail::operator_equal_impl(lhs, lhs.length(), rhs, N);
482 }
483 
484 template<
485  class T,
486  class Array,
487  class = typename std::enable_if<
488  is_array<Array>::value
489  >::type
490 >
491 inline constexpr
492 bool operator!=(array_view<T> const& lhs, Array const& rhs)
493 {
494  return !(lhs == rhs);
495 }
496 
497 template<
498  class Array,
499  class T,
500  class = typename std::enable_if<
501  is_array<Array>::value
502  >::type
503 >
504 inline constexpr
505 bool operator==(Array const& lhs, array_view<T> const& rhs)
506 {
507  return rhs == lhs;
508 }
509 
510 template<
511  class Array,
512  class T,
513  class = typename std::enable_if<
514  is_array<Array>::value,
515  Array
516  >::type
517 >
518 inline constexpr
519 bool operator!=(Array const& lhs, array_view<T> const& rhs)
520 {
521  return !(rhs == lhs);
522 }
523 // }}}
524 
525 // helpers to construct view {{{
526 template<
527  class Array,
528  class = typename std::enable_if<
530  >::type
531 >
532 inline constexpr
533 auto make_view(Array const& a)
535 {
536  return {a};
537 }
538 
539 template< class T, size_t N>
540 inline constexpr
542 {
543  return {a};
544 }
545 
546 template<class T>
547 inline constexpr
549 {
550  return array_view<T>{p, n};
551 }
552 
553 template<class InputIterator, class Result = array_view<typename std::iterator_traits<InputIterator>::value_type>>
554 inline constexpr
555 Result make_view(InputIterator begin, InputIterator end)
556 {
557  return Result{begin, end};
558 }
559 
560 template<class T>
561 inline constexpr
562 array_view<T> make_view(std::initializer_list<T> const& l)
563 {
564  return {l};
565 }
566 // }}}
567 
568 } // inline namespace __ROOT
569 } // namespace std
570 
571 
572 
573 
574 
575 #if 0
576 // This stuff is too complex for our simple use case!
577 
578 #include <cstddef>
579 #include <array>
580 #include <type_traits>
581 
582 // See N3851
583 
584 namespace std {
585 
586 template<int Rank>
587 class index;
588 
589 template<int Rank>
590 class bounds {
591 public:
592  static constexpr int rank = Rank;
593  using reference = ptrdiff_t &;
594  using const_reference = const ptrdiff_t &;
595  using size_type = size_t;
596  using value_type = ptrdiff_t;
597 
598 private:
599  std::array<value_type, Rank> m_B;
600 
601 public:
602  constexpr bounds() noexcept;
603 
604  constexpr bounds(value_type b) noexcept: m_B{{b}} { };
605  //constexpr bounds(const initializer_list<value_type>&) noexcept;
606  //constexpr bounds(const bounds&) noexcept;
607  //bounds& operator=(const bounds&) noexcept;
608 
609  reference operator[](size_type i) noexcept { return m_B[i]; }
610 
611  constexpr const_reference operator[](
612  size_type i) const noexcept { return m_B[i]; };
613 
614 
615  bool operator==(const bounds &rhs) const noexcept;
616 
617  bool operator!=(const bounds &rhs) const noexcept;
618 
619  bounds operator+(const index<rank> &rhs) const noexcept;
620 
621  bounds operator-(const index<rank> &rhs) const noexcept;
622 
623  bounds &operator+=(const index<rank> &rhs) noexcept;
624 
625  bounds &operator-=(const index<rank> &rhs) noexcept;
626 
627  constexpr size_type size() const noexcept;
628 
629  bool contains(const index<rank> &idx) const noexcept;
630  //bounds_iterator<rank> begin() const noexcept;
631  //bounds_iterator<rank> end() const noexcept;
632 
633 };
634 
635 //bounds operator+(const index<rank>& lhs, const bounds& rhs) noexcept;
636 
637 template<int Rank>
638 class index {
639 public:
640  static constexpr int rank = Rank;
641  using reference = ptrdiff_t &;
642  using const_reference = const ptrdiff_t &;
643  using size_type = size_t;
644  using value_type = ptrdiff_t;
645 
646 // For index<rank>:
647  constexpr index() noexcept;
648 
649  constexpr index(value_type) noexcept;
650 
651  constexpr index(const initializer_list<value_type> &) noexcept;
652 
653  constexpr index(const index &) noexcept;
654 
655  index &operator=(const index &) noexcept;
656 
657  reference operator[](size_type component_idx) noexcept;
658 
659  constexpr const_reference operator[](size_type component_idx) const noexcept;
660 
661  bool operator==(const index &rhs) const noexcept;
662 
663  bool operator!=(const index &rhs) const noexcept;
664 
665  index operator+(const index &rhs) const noexcept;
666 
667  index operator-(const index &rhs) const noexcept;
668 
669  index &operator+=(const index &rhs) noexcept;
670 
671  index &operator-=(const index &rhs) noexcept;
672 
673  index &operator++() noexcept;
674 
675  index operator++(int) noexcept;
676 
677  index &operator--() noexcept;
678 
679  index operator--(int) noexcept;
680 
681  index operator+() const noexcept;
682 
683  index operator-() const noexcept;
684 };
685 
686 /// Mock-up of future atd::(experimental::)array_view.
687 /// Supports only what we need for THist, e.g. Rank := 1.
688 template<typename ValueType, int Rank = 1>
689 class array_view {
690 public:
691  static constexpr int rank = Rank;
692  using index_type = index<rank>;
693  using bounds_type = bounds<rank>;
694  using size_type = typename bounds_type::size_type;
695  using value_type = ValueType;
696  using pointer = typename std::add_pointer_t<value_type>;
697  using reference = typename std::add_lvalue_reference_t<value_type>;
698 
699  constexpr array_view() noexcept;
700 
701  constexpr explicit array_view(std::vector<ValueType> &cont) noexcept;
702 
703  template<typename ArrayType>
704  constexpr explicit array_view(ArrayType &data) noexcept;
705 
706  template<typename ViewValueType>
707  constexpr array_view(const array_view<ViewValueType, rank> &rhs) noexcept;
708 
709  template<typename Container>
710  constexpr array_view(bounds_type bounds, Container &cont) noexcept;
711 
712  constexpr array_view(bounds_type bounds, pointer data) noexcept;
713 
714  template<typename ViewValueType>
716 
717  constexpr bounds_type bounds() const noexcept;
718  constexpr size_type size() const noexcept;
719  constexpr index_type stride() const noexcept;
720 
721  constexpr pointer data() const noexcept;
722  constexpr reference operator[](const index_type& idx) const noexcept;
723 };
724 
725 }
726 #endif // too complex!
727 #endif
constexpr const_pointer data() const noexcept
array_view(InputIterator start, InputIterator last)
constexpr array_view< T > slice_before(iterator const pos) const
constexpr const_iterator cbegin() const noexcept
double T(double x)
Definition: ChebyshevPol.h:34
std::reverse_iterator< const_iterator > const_reverse_iterator
constexpr const_iterator end() const noexcept
constexpr array_view() noexcept
constexpr array_view< T > slice(check_bound_t, iterator start, iterator last) const
static constexpr check_bound_t check_bound
void Step(const gsl_rng *r, void *xp, double step_size)
constexpr size_type max_size() const noexcept
#define N
value_type const * pointer
constexpr const_iterator begin() const noexcept
const_reverse_iterator rend() const
STL namespace.
value_type const & reference
#define R__CONSTEXPR_IF_CXX14
constexpr const_reference operator[](size_type const n) const noexcept
constexpr array_view< T > slice_before(size_type const pos) const
array_view(std::initializer_list< T > const &l)
constexpr const_iterator cend() const noexcept
constexpr array_view< T > slice_before(check_bound_t, iterator const pos) const
constexpr array_view(T const *a, size_type const n) noexcept
constexpr array_view< T > slice(size_type const pos, size_type const slicelen) const
TTime operator-(const TTime &t1, const TTime &t2)
Definition: TTime.h:83
std::reverse_iterator< iterator > reverse_iterator
constexpr auto make_view(Array const &a) -> array_view< typename Array::value_type >
TString operator+(const TString &s1, const TString &s2)
Use the special concatenation constructor.
Definition: TString.cxx:1448
auto to_vector(Allocator const &alloc=Allocator{}) const -> std::vector< T, Allocator >
const_reverse_iterator crend() const
array_view(std::vector< T > const &v) noexcept
value_type const * iterator
auto to_array_impl(detail::indices< I... >) const -> std::array< T, sizeof...(I)>
constexpr array_view< T > slice_after(check_bound_t, iterator const pos) const
R__CONSTEXPR_IF_CXX14 bool operator_equal_impl(ArrayL const &lhs, size_t const lhs_size, ArrayR const &rhs, size_t const rhs_size)
constexpr const_reference back() const noexcept
const_pointer const data_
const char * Array
SVector< double, 2 > v
Definition: Dict.h:5
auto * a
Definition: textangle.C:12
const_reverse_iterator rbegin() const
constexpr bool operator==(array_view< T1 > const &lhs, array_view< T2 > const &rhs)
constexpr array_view< T > slice_after(iterator const pos) const
constexpr array_view< T > slice_after(check_bound_t, size_type const pos) const
constexpr array_view< T > slice_after(size_type const pos) const
int type
Definition: TGX11.cxx:120
#define T2
Definition: md5.inl:146
constexpr array_view< T > slice_before(check_bound_t, size_type const pos) const
array_view(T const (&a)[N]) noexcept
typename make_indices_< Start, Last, Step >::type make_indices
constexpr array_view< T > slice(check_bound_t, size_type const pos, size_type const slicelen) const
value_type const * const_pointer
Binding & operator=(OUT(*fun)(void))
auto to_array() const -> std::array< T, N >
constexpr size_type length() const noexcept
constexpr const_reference at(size_type const n) const
constexpr bool empty() const noexcept
constexpr array_view< T > slice(iterator start, iterator last) const
auto * l
Definition: textangle.C:4
you should not use this method at all Int_t Int_t Double_t Double_t Double_t Int_t Double_t Double_t Double_t Double_t b
Definition: TRolke.cxx:630
value_type const * const_iterator
constexpr size_type size() const noexcept
#define I(x, y, z)
constexpr bool operator!=(array_view< T > const &lhs, Array const &rhs)
value_type const & const_reference
const Int_t n
Definition: legend1.C:16
array_view(std::array< T, N > const &a) noexcept
constexpr const_reference front() const noexcept
const_reverse_iterator crbegin() const
std::string & operator+=(std::string &left, const TString &right)
Definition: TString.h:454