try {
      f();
      g();
    } catch (S s) {
      // expected-error@-2 {{variable of type 'dr479::S' has private destructor}}
    }

// FIXME: __SIZE_TYPE__ expands to 'long long' on some targets.
__extension__ typedef __SIZE_TYPE__ size_t;

namespace std { struct type_info; }

namespace dr400 { // dr400: yes
  struct A { int a; struct a {}; }; // expected-note 2{{conflicting}} expected-note {{ambiguous}}
  struct B { int a; struct a {}; }; // expected-note 2{{target}} expected-note {{ambiguous}}
  struct C : A, B { using A::a; struct a b; };
  struct D : A, B { using A::a; using B::a; struct a b; }; // expected-error 2{{conflicts}}
  struct E : A, B { struct a b; }; // expected-error {{found in multiple base classes}}
}

namespace dr401 { // dr401: yes
  template<class T, class U = typename T::type> class A : public T {}; // expected-error {{protected}} expected-error 2{{private}}

  class B {
  protected:
    typedef int type; // expected-note {{protected}}
#if __cplusplus == 199711L
#endif
  };

  class C {
    typedef int type; // expected-note {{private}}
    friend class A<C>; // expected-note {{default argument}}
  };

  class D {
    typedef int type; // expected-note {{private}}
    friend class A<D, int>;
  };

  A<B> *b; // expected-note {{default argument}}
  A<D> *d; // expected-note {{in instantiation of default argument}}

  struct E {
    template<class T, class U = typename T::type> class A : public T {};
  };
  class F {
    typedef int type;
    friend class E;
  };
  E::A<F> eaf; // ok, default argument is in befriended context

  // FIXME: Why do we get different diagnostics in C++11 onwards here? We seem
  // to not treat the default template argument as a SFINAE context in C++98.
  template<class T, class U = typename T::type> void f(T) {}
  void g(B b) { f(b); }
#if __cplusplus < 201103L
#else
#endif
}

namespace dr403 { // dr403: yes
  namespace A {
    struct S {};
    int f(void*);
  }
  template<typename T> struct X {};
  typedef struct X<A::S>::X XS;
  XS *p;
  int k = f(p); // ok, finds A::f, even though type XS is a typedef-name
                // referring to an elaborated-type-specifier naming a
                // injected-class-name, which is about as far from a
                // template-id as we can make it.
}

// dr404: na

namespace dr406 { // dr406: yes
  typedef struct {
    static int n; // expected-error {{static data member 'n' not allowed in anonymous struct}}
  } A;
  typedef union {
    static int n; // expected-error {{static data member 'n' not allowed in anonymous union}}
  } B;
}

namespace dr407 { // dr407: 3.8
  struct S;
  typedef struct S S;
  void f() {
    struct S *p;
    {
      typedef struct S S; // expected-note {{here}}
      struct S *p; // expected-error {{typedef 'S' cannot be referenced with a struct specifier}}
    }
  }
  struct S {};

  namespace UsingDir {
    namespace A {
      struct S {}; // expected-note {{found}}
    }
    namespace B {
      typedef int S; // expected-note {{found}}
    }
    namespace C {
      using namespace A;
      using namespace B;
      struct S s; // expected-error {{ambiguous}}
    }
    namespace D {
      using A::S;
      typedef struct S S;
      struct S s;
    }
    namespace E {
      // The standard doesn't say whether this is valid. We interpret
      // DR407 as meaning "if lookup finds both a tag and a typedef with the
      // same type, then it's OK in an elaborated-type-specifier".
      typedef A::S S;
      using A::S;
      struct S s;
    }
    namespace F {
      typedef A::S S;
    }
    // The standard doesn't say what to do in these cases either.
    namespace G {
      using namespace A;
      using namespace F;
      struct S s;
    }
    namespace H {
      using namespace F;
      using namespace A;
      struct S s;
    }
  }
}

namespace dr408 { // dr408: 3.4
  template<int N> void g() { int arr[N != 1 ? 1 : -1]; }
  template<> void g<2>() { }

  template<typename T> struct S {
    static int i[];
    void f();
  };
  template<typename T> int S<T>::i[] = { 1 };

  template<typename T> void S<T>::f() {
    g<sizeof (i) / sizeof (int)>();
  }
  template<> int S<int>::i[] = { 1, 2 };
  template void S<int>::f(); // uses g<2>(), not g<1>().

  template<typename T> struct R {
    static int arr[];
    void f();
  };
  template<typename T> int R<T>::arr[1];
  template<typename T> void R<T>::f() {
    int arr[sizeof(arr) != sizeof(int) ? 1 : -1];
  }
  template<> int R<int>::arr[2];
  template void R<int>::f();
}

namespace dr409 { // dr409: yes
  template<typename T> struct A {
    typedef int B;
    B b1;
    A::B b2;
    A<T>::B b3;
    A<T*>::B b4; // expected-error {{missing 'typename'}}
  };
}

namespace dr410 { // dr410: no
  template<class T> void f(T);
  void g(int);
  namespace M {
    template<class T> void h(T);
    template<class T> void i(T);
    struct A {
      friend void f<>(int);
      friend void h<>(int);
      friend void g(int);
      template<class T> void i(T);
      friend void i<>(int);
    private:
      static void z(); // expected-note {{private}}
    };

    template<> void h(int) { A::z(); }
    // FIXME: This should be ill-formed. The member A::i<> is befriended,
    template<> void i(int) { A::z(); }
  }
  template<> void f(int) { M::A::z(); }
  void g(int) { M::A::z(); } // expected-error {{private}}
}

// dr412 is in its own file.

namespace dr413 { // dr413: yes
  struct S {
    int a;
    int : 17;
    int b;
  };
  S s = { 1, 2, 3 }; // expected-error {{excess elements}}

  struct E {};
  struct T { // expected-note {{here}}
    int a;
    E e;
    int b;
  };
  T t1 = { 1, {}, 2 };
  T t2 = { 1, 2 }; // expected-error {{aggregate with no elements requires explicit braces}}
}

namespace dr414 { // dr414: dup 305
  struct X {};
  void f() {
    X x;
    struct X {};
    x.~X();
  }
}

namespace dr415 { // dr415: yes
  template<typename T> void f(T, ...) { T::error; }
  void f(int, int);
  void g() { f(0, 0); } // ok
}

namespace dr416 { // dr416: yes
  extern struct A a;
  int &operator+(const A&, const A&);
  int &k = a + a;
  struct A { float &operator+(A&); };
  float &f = a + a;
}

namespace dr417 { // dr417: no
  struct A;
  struct dr417::A {}; // expected-warning {{extra qualification}}
  struct B { struct X; };
  struct C : B {};
  struct C::X {}; // expected-error {{no struct named 'X' in 'dr417::C'}}
  struct B::X { struct Y; };
  struct C::X::Y {}; // ok!
  namespace N {
    struct D;
    struct E;
    struct F;
    struct H;
  }
  // FIXME: This is ill-formed.
  using N::D;
  struct dr417::D {}; // expected-warning {{extra qualification}}
  using namespace N;
  struct dr417::E {}; // expected-warning {{extra qualification}} expected-error {{no struct named 'E'}}
  struct N::F {};
  struct G;
  using N::H;
  namespace M {
    struct dr417::G {}; // expected-error {{namespace 'M' does not enclose}}
    struct dr417::H {}; // expected-error {{namespace 'M' does not enclose}}
  }
}

namespace dr420 { // dr420: yes
  template<typename T> struct ptr {
    T *operator->() const;
    T &operator*() const;
  };
  template<typename T, typename P> void test(P p) {
    p->~T();
    p->T::~T();
    (*p).~T();
    (*p).T::~T();
  }
  struct X {};
  template void test<int>(int*);
  template void test<int>(ptr<int>);
  template void test<X>(X*);
  template void test<X>(ptr<X>);

  template<typename T>
  void test2(T p) {
    p->template Y<int>::~Y<int>();
    p->~Y<int>();
    p->template ~Y<int>(); // expected-error {{'template' keyword not permitted in destructor name}}
  }
  template<typename T> struct Y {};
  template void test2(Y<int>*);
  template void test2(ptr<Y<int> >);

  void test3(int *p, ptr<int> q) {
    typedef int Int;
    p->~Int();
    q->~Int();
    p->Int::~Int();
    q->Int::~Int();
  }

#if __cplusplus >= 201103L
  template<typename T> using id = T;
  struct A { template<typename T> using id = T; };
  void test4(int *p, ptr<int> q) {
    p->~id<int>();
    q->~id<int>();
    p->id<int>::~id<int>();
    q->id<int>::~id<int>();
    p->template id<int>::~id<int>(); // OK since dr2292
    q->template id<int>::~id<int>(); // OK since dr2292
    p->A::template id<int>::~id<int>();
    q->A::template id<int>::~id<int>();
  }
#endif
}

namespace dr421 { // dr421: yes
  struct X { X(); int n; int &r; };
  int *p = &X().n; // expected-error-re {{{{taking the address of a temporary|cannot take the address of an rvalue}}}}
  int *q = &X().r;
}

namespace dr422 { // dr422: yes
  template<typename T, typename U> void f() {
    typedef T type; // expected-note {{prev}}
    typedef U type; // expected-error {{redef}}
  }
  template void f<int, int>();
  template void f<int, char>(); // expected-note {{instantiation}}
}

namespace dr423 { // dr423: yes
  template<typename T> struct X { operator T&(); };
  void f(X<int> x) { x += 1; }
}

namespace dr424 { // dr424: yes
  struct A {
    typedef int N; // expected-note {{previous}}
    typedef int N; // expected-error {{redefinition}}

    struct X;
    typedef X X; // expected-note {{previous}}
    struct X {};

    struct X *p;
    struct A::X *q;
    X *r;

    typedef X X; // expected-error {{redefinition}}
  };
  struct B {
    typedef int N;
  };
  struct C : B {
    typedef int N; // expected-note {{previous}}
    typedef int N; // expected-error {{redefinition}}
  };
}

namespace dr425 { // dr425: yes
  struct A { template<typename T> operator T() const; } a;
  float f = 1.0f * a; // expected-error {{ambiguous}} expected-note 5+{{built-in candidate}}

  template<typename T> struct is_float;
  template<> struct is_float<float> { typedef void type; };

  struct B {
    template<typename T, typename U = typename is_float<T>::type> operator T() const; // expected-error 0-1{{extension}}
  } b;
  float g = 1.0f * b; // ok
}

namespace dr427 { // dr427: yes
  struct B {};
  struct D : public B {
    D(B &) = delete; // expected-error 0-1{{extension}} expected-note {{deleted}}
  };

  extern D d1;
  B &b = d1;
  const D &d2 = static_cast<const D&>(b);
  const D &d3 = (const D&)b;
  const D &d4(b); // expected-error {{deleted}}
}

namespace dr428 { // dr428: yes
  template<typename T> T make();
  extern struct X x; // expected-note 5{{forward declaration}}
  void f() {
    throw void(); // expected-error {{cannot throw}}
    throw make<void*>();
    throw make<const volatile void*>();
    throw x; // expected-error {{cannot throw}}
    throw make<X&>(); // expected-error {{cannot throw}}
    throw make<X*>(); // expected-error {{cannot throw}}
    throw make<const volatile X&>(); // expected-error {{cannot throw}}
    throw make<const volatile X*>(); // expected-error {{cannot throw}}
  }
}

namespace dr429 { // dr429: yes c++11
  // FIXME: This rule is obviously intended to apply to C++98 as well.
  struct A {
    static void *operator new(size_t, size_t);
    static void operator delete(void*, size_t);
  } *a = new (0) A;
#if __cplusplus >= 201103L
  // expected-error@-2 {{'new' expression with placement arguments refers to non-placement 'operator delete'}}
#endif
  struct B {
    static void *operator new(size_t, size_t);
    static void operator delete(void*);
    static void operator delete(void*, size_t);
  } *b = new (0) B; // ok, second delete is not a non-placement deallocation function
}

namespace dr430 { // dr430: yes c++11
  // resolved by n2239
  // FIXME: This should apply in C++98 too.
  void f(int n) {
    int a[] = { n++, n++, n++ };
#if __cplusplus < 201103L
    // expected-warning@-2 {{multiple unsequenced modifications to 'n'}}
#endif
  }
}

namespace dr431 { // dr431: yes
  struct A {
    template<typename T> T *get();
    template<typename T> struct B {
      template<typename U> U *get();
    };
  };

  template<typename T> void f(A a) {
    a.get<A>()->get<T>();
    a.get<T>()
        ->get<T>(); // expected-error {{use 'template'}}
    a.get<T>()->template get<T>();
    a.A::get<T>();
    A::B<int> *b = a.get<A::B<int> >();
    b->get<int>();
    b->A::B<int>::get<int>();
    b->A::B<int>::get<T>();
    b->A::B<T>::get<int>(); // expected-error {{use 'template'}}
    b->A::B<T>::template get<int>();
    b->A::B<T>::get<T>(); // expected-error {{use 'template'}}
    b->A::B<T>::template get<T>();
    A::B<T> *c = a.get<A::B<T> >();
    c->get<int>(); // expected-error {{use 'template'}}
    c->template get<int>();
  }
}

namespace dr432 { // dr432: yes
  template<typename T> struct A {};
  template<typename T> struct B : A<B> {}; // expected-error {{requires template arguments}} expected-note {{declared}}
  template<typename T> struct C : A<C<T> > {};
#if __cplusplus >= 201103L
  template<typename T> struct D : decltype(A<D>()) {}; // expected-error {{requires template arguments}} expected-note {{declared}}
#endif
}

namespace dr433 { // dr433: yes
  template<class T> struct S {
    void f(union U*);
  };
  U *p;
  template<class T> void S<T>::f(union U*) {}

  S<int> s;
}

namespace dr434 { // dr434: sup 2352
  void f() {
    const int ci = 0;
    int *pi = 0;
    const int *&rpci = pi; // expected-error {{incompatible qualifiers}}
    const int * const &rcpci = pi; // OK
    rpci = &ci;
    *pi = 1;
  }

#if __cplusplus >= 201103L
  int *pi = 0;
  const int * const &rcpci = pi;
  static_assert(&rcpci == &pi, "");
#endif
}

// dr435: na

namespace dr436 { // dr436: yes
  enum E { f }; // expected-note {{previous}}
  void f(); // expected-error {{redefinition}}
}

namespace dr437 { // dr437: sup 1308
  // This is superseded by 1308, which is in turn superseded by 1330,
  // which restores this rule.
  template<typename U> struct T : U {};
  struct S {
    void f() throw(S);
#if __cplusplus > 201402L
    // expected-error@-2 {{ISO C++17 does not allow}} expected-note@-2 {{use 'noexcept}}
#endif
    void g() throw(T<S>);
#if __cplusplus > 201402L
    // expected-error@-2 {{ISO C++17 does not allow}} expected-note@-2 {{use 'noexcept}}
#endif
    struct U;
    void h() throw(U);
#if __cplusplus > 201402L
    // expected-error@-2 {{ISO C++17 does not allow}} expected-note@-2 {{use 'noexcept}}
#endif
    struct U {};
  };
}

// dr438 FIXME write a codegen test
// dr439 FIXME write a codegen test
// dr441 FIXME write a codegen test
// dr443: na

namespace dr444 { // dr444: yes
  struct D;
  struct B {                    // expected-note {{candidate function (the implicit copy}} expected-note 0-1 {{implicit move}}
    D &operator=(D &) = delete; // expected-error 0-1{{extension}} expected-note {{deleted}}
  };
  struct D : B { // expected-note {{candidate function (the implicit copy}} expected-note 0-1 {{implicit move}}
    using B::operator=;
  } extern d;
  void f() {
    d = d; // expected-error {{deleted}}
  }
}

namespace dr445 { // dr445: yes
  class A { void f(); }; // expected-note {{private}}
  struct B {
    friend void A::f(); // expected-error {{private}}
  };
}

namespace dr446 { // dr446: yes
  struct C;
  struct A {
    A();
    A(const A&) = delete; // expected-error 0-1{{extension}} expected-note +{{deleted}}
    A(const C&);
  };
  struct C : A {};
  void f(A a, bool b, C c) {
    void(b ? a : a);
    b ? A() : a; // expected-error {{deleted}}
    b ? a : A(); // expected-error {{deleted}}
    b ? A() : A();
#if __cplusplus <= 201402L
#endif

    void(b ? a : c);
    b ? a : C(); // expected-error {{deleted}}
    b ? c : A();
#if __cplusplus <= 201402L
#endif
    b ? A() : C();
#if __cplusplus <= 201402L
#endif
  }
}

namespace dr447 { // dr447: yes
  struct A { int n; int a[4]; };
  template<int> struct U {
    typedef int type;
    template<typename V> static void h();
  };
  template<typename T> U<sizeof(T)> g(T);
  template<typename T, int N> void f(int n) {
    // ok, not type dependent
    g(__builtin_offsetof(A, n)).h<int>();
    g(__builtin_offsetof(T, n)).h<int>();
    // value dependent if first argument is a dependent type
    U<__builtin_offsetof(A, n)>::type a;
    U<__builtin_offsetof(T, n)>::type b; // expected-error +{{}} expected-warning 0+{{}}
    // as an extension, we allow the member-designator to include array indices
    g(__builtin_offsetof(A, a[0])).h<int>();
    g(__builtin_offsetof(A, a[N])).h<int>();
    U<__builtin_offsetof(A, a[0])>::type c;
    U<__builtin_offsetof(A, a[N])>::type d; // expected-error +{{}} expected-warning 0+{{}}
  }
}

namespace dr448 { // dr448: yes
  template<typename T = int> void f(int); // expected-error 0-1{{extension}} expected-note {{no known conversion}}
  template<typename T> void g(T t) {
    f<T>(t); // expected-error {{neither visible in the template definition nor found by argument-dependent lookup}}
    dr448::f(t); // expected-error {{no matching function}}
  }
  template<typename T> void f(T); // expected-note {{should be declared prior to the call site}}
  namespace HideFromADL { struct X {}; }
  template void g(int); // ok
  template void g(HideFromADL::X); // expected-note {{instantiation of}}
}

// dr449: na

namespace dr450 { // dr450: yes
  typedef int A[3];
  void f1(const A &);
  void f2(A &); // expected-note +{{not viable}}
  struct S { A n; };
  void g() {
    f1(S().n);
    f2(S().n); // expected-error {{no match}}}
  }
#if __cplusplus >= 201103L
  void h() {
    f1(A{});
    f2(A{}); // expected-error {{no match}}
  }
#endif
}

namespace dr451 { // dr451: yes
  const int a = 1 / 0; // expected-warning {{undefined}}
  const int b = 1 / 0; // expected-warning {{undefined}}
  int arr[b]; // expected-error +{{variable length arr}}
}

namespace dr452 { // dr452: yes
  struct A {
    int a, b, c;
    A *p;
    int f();
    A() : a(f()), b(this->f() + a), c(this->a), p(this) {}
  };
}

// dr454 FIXME write a codegen test

namespace dr456 { // dr456: yes
  const int null = 0;
  void *p = null;
#if __cplusplus >= 201103L
#else
#endif

  const bool f = false;
  void *q = f;
#if __cplusplus >= 201103L
#else
#endif
}

namespace dr457 { // dr457: yes
  const int a = 1;
  const volatile int b = 1;
  int ax[a];
  int bx[b]; // expected-error +{{variable length array}}

  enum E {
    ea = a,
    eb = b // expected-error {{constant}} expected-note {{read of volatile-qualified}}
  };
}

namespace dr458 { // dr458: 11
  struct A {
    int T;
    int f();
    template<typename> int g();
  };

  template<typename> struct B : A {
    int f();
    template<typename> int g();
    template<typename> int h();
  };

  int A::f() {
    return T;
  }
  template<typename T> // expected-note {{declared here}}
  int A::g() {
    return T; // expected-error {{'T' does not refer to a value}}
  }

  template<typename T>
  int B<T>::f() {
    return T;
  }
  template<typename T> template<typename U>
  int B<T>::g() {
    return T;
  }
  template<typename U> template<typename T> // expected-note {{declared here}}
  int B<U>::h() {
    return T; // expected-error {{'T' does not refer to a value}}
  }
}

namespace dr460 { // dr460: yes
  namespace X { namespace Q { int n; } }
  namespace Y {
    using X; // expected-error {{requires a qualified name}}
    using dr460::X; // expected-error {{cannot refer to a namespace}}
    using X::Q; // expected-error {{cannot refer to a namespace}}
  }
}

// dr461: na
// dr462 FIXME write a codegen test
// dr463: na
// dr464: na
// dr465: na

namespace dr466 { // dr466: no
  typedef int I;
  typedef const int CI;
  typedef volatile int VI;
  void f(int *a, CI *b, VI *c) {
    a->~I();
    a->~CI();
    a->~VI();
    a->I::~I();
    a->CI::~CI();
    a->VI::~VI();

    a->CI::~VI(); // FIXME: This is invalid; CI and VI are not the same scalar type.

    b->~I();
    b->~CI();
    b->~VI();
    b->I::~I();
    b->CI::~CI();
    b->VI::~VI();

    c->~I();
    c->~CI();
    c->~VI();
    c->I::~I();
    c->CI::~CI();
    c->VI::~VI();
  }
}

namespace dr467 { // dr467: yes
  int stuff();

  int f() {
    static bool done;
    if (done)
      goto later;
    static int k = stuff();
    done = true;
  later:
    return k;
  }
  int g() {
    goto later; // expected-error {{cannot jump}}
    int k = stuff(); // expected-note {{bypasses variable initialization}}
  later:
    return k;
  }
}

namespace dr468 { // dr468: yes c++11
  // FIXME: Should we allow this in C++98 too?
  template<typename> struct A {
    template<typename> struct B {
      static int C;
    };
  };
  int k = dr468::template A<int>::template B<char>::C;
#if __cplusplus < 201103L
  // expected-error@-2 2{{'template' keyword outside of a template}}
#endif
}

namespace dr469 { // dr469: no
  template<typename T> struct X; // expected-note {{here}}
  template<typename T> struct X<const T> {};
  X<int&> x; // expected-error {{undefined}}
}

namespace dr470 { // dr470: yes
  template<typename T> struct A {
    struct B {};
  };
  template<typename T> struct C {
  };

  template struct A<int>; // expected-note {{previous}}
  template struct A<int>::B; // expected-error {{duplicate explicit instantiation}}

  // ok, instantiating C<char> doesn't instantiate base class members.
  template struct A<char>;
  template struct C<char>;
}

namespace dr471 { // dr471: yes
  struct A { int n; };
  struct B : private virtual A {};
  struct C : protected virtual A {};
  struct D : B, C { int f() { return n; } };
  struct E : private virtual A {
    using A::n;
  };
  struct F : E, B { int f() { return n; } };
  struct G : virtual A {
  private:
    using A::n; // expected-note {{here}}
  };
  struct H : B, G { int f() { return n; } }; // expected-error {{private}}
}

namespace dr474 { // dr474: yes
  namespace N {
    struct S {
      void f();
    };
  }
  void N::S::f() {
    void g(); // expected-note {{previous}}
  }
  int g();
  namespace N {
    int g(); // expected-error {{cannot be overloaded}}
  }
}

// dr475 FIXME write a codegen test

namespace dr477 { // dr477: 3.5
  struct A {
    explicit A();
    virtual void f();
  };
  struct B {
    friend explicit A::A(); // expected-error {{'explicit' is invalid in friend declarations}}
    friend virtual void A::f(); // expected-error {{'virtual' is invalid in friend declarations}}
  };
  explicit A::A() {} // expected-error {{can only be specified inside the class definition}}
  virtual void A::f() {} // expected-error {{can only be specified inside the class definition}}
}

namespace dr478 { // dr478: yes
  struct A { virtual void f() = 0; }; // expected-note {{unimplemented}}
  void f(A *a);
  void f(A a[10]); // expected-error {{array of abstract class type}}
}

namespace dr479 { // dr479: yes
  struct S {
    S();
  private:
    S(const S&); // expected-note +{{here}}
    ~S(); // expected-note +{{here}}
  };
  void f() {
    throw S();
    // expected-error@-1 {{temporary of type 'dr479::S' has private destructor}}
    // expected-error@-2 {{exception object of type 'dr479::S' has private destructor}}
#if __cplusplus < 201103L
    // expected-error@-4 {{C++98 requires an accessible copy constructor}}
#endif
#if __cplusplus <= 201402L
#endif
  }
  void g() {
    S s; // expected-error {{private destructor}}}
    throw s;
    // expected-error@-2 {{exception object of type 'dr479::S' has private destructor}}
  }
  void h() {

  }
}

namespace dr480 { // dr480: yes
  struct A { int n; };
  struct B : A {};
  struct C : virtual B {};
  struct D : C {};

  int A::*a = &A::n;
  int D::*b = a; // expected-error {{virtual base}}

  extern int D::*c;
  int A::*d = static_cast<int A::*>(c); // expected-error {{virtual base}}

  D *e;
  A *f = e;
  D *g = static_cast<D*>(f); // expected-error {{virtual base}}

  extern D &i;
  A &j = i;
  D &k = static_cast<D&>(j); // expected-error {{virtual base}}
}

namespace dr481 { // dr481: yes
  template<class T, T U> class A { T *x; };
  T *x; // expected-error {{unknown type}}

  template<class T *U> class B { T *x; };
  T *y; // ok

  struct C {
    template<class T> void f(class D *p);
  };
  D *z; // ok

  template<typename A = C, typename C = A> struct E {
    void f() {
      typedef ::dr481::C c; // expected-note {{previous}}
      typedef C c; // expected-error {{different type}}
    }
  };
  template struct E<>; // ok
  template struct E<int>; // expected-note {{instantiation of}}

  template<template<typename U_no_typo_correction> class A,
           A<int> *B,
           U_no_typo_correction *C> // expected-error {{unknown type}}
  struct F {
    U_no_typo_correction *x; // expected-error {{unknown type}}
  };

  template<template<class H *> class> struct G {
    H *x;
  };
  H *q;

  typedef int N;
  template<N X, typename N, template<N Y> class T> struct I;
  template<char*> struct J;
  I<123, char*, J> *j;
}

namespace dr482 { // dr482: 3.5
  extern int a;
  void f();

  int dr482::a = 0; // expected-warning {{extra qualification}}
  void dr482::f() {} // expected-warning {{extra qualification}}

  inline namespace X { // expected-error 0-1{{C++11 feature}}
    extern int b;
    void g();
    struct S;
  }
  int dr482::b = 0; // expected-warning {{extra qualification}}
  void dr482::g() {} // expected-warning {{extra qualification}}
  struct dr482::S {}; // expected-warning {{extra qualification}}

  void dr482::f(); // expected-warning {{extra qualification}}
  void dr482::g(); // expected-warning {{extra qualification}}

  // FIXME: The following are valid in DR482's wording, but these are bugs in
  // the wording which we deliberately don't implement.
  namespace N { typedef int type; }
  typedef int N::type; // expected-error {{typedef declarator cannot be qualified}}
  struct A {
    struct B;
    struct A::B {}; // expected-error {{extra qualification}}

#if __cplusplus >= 201103L
    enum class C;
    enum class A::C {}; // expected-error {{extra qualification}}
#endif
  };
}

namespace dr483 { // dr483: yes
  namespace climits {
    int check1[__SCHAR_MAX__ >= 127 ? 1 : -1];
    int check2[__SHRT_MAX__ >= 32767 ? 1 : -1];
    int check3[__INT_MAX__ >= 32767 ? 1 : -1];
    int check4[__LONG_MAX__ >= 2147483647 ? 1 : -1];
    int check5[__LONG_LONG_MAX__ >= 9223372036854775807 ? 1 : -1];
#if __cplusplus < 201103L
#endif
  }
  namespace cstdint {
    int check1[__PTRDIFF_WIDTH__ >= 16 ? 1 : -1];
    int check2[__SIG_ATOMIC_WIDTH__ >= 8 ? 1 : -1];
    int check3[__SIZE_WIDTH__ >= 16 ? 1 : -1];
    int check4[__WCHAR_WIDTH__ >= 8 ? 1 : -1];
    int check5[__WINT_WIDTH__ >= 16 ? 1 : -1];
  }
}

namespace dr484 { // dr484: yes
  struct A {
    A();
    void f();
  };
  typedef const A CA;
  void CA::f() {
    this->~CA();
    this->CA::~A();
    this->CA::A::~A();
  }
  CA::A() {}

  struct B : CA {
    B() : CA() {}
    void f() { return CA::f(); }
  };

  struct C;
  typedef C CT; // expected-note {{here}}
  struct CT {}; // expected-error {{conflicts with typedef}}

  namespace N {
    struct D;
    typedef D DT; // expected-note {{here}}
  }
  struct N::DT {}; // expected-error {{conflicts with typedef}}

  typedef struct {
    S(); // expected-error {{requires a type}}
  } S;
}

namespace dr485 { // dr485: yes
  namespace N {
    struct S {};
    int operator+(S, S);
    template<typename T> int f(S);
  }
  template<typename T> int f();

  N::S s;
  int a = operator+(s, s);
  int b = f<int>(s);
}

namespace dr486 { // dr486: yes
  template<typename T> T f(T *); // expected-note 2{{substitution failure}}
  int &f(...);

  void g();
  int n[10];

  void h() {
    int &a = f(&g);
    int &b = f(&n);
    f<void()>(&g); // expected-error {{no match}}
    f<int[10]>(&n); // expected-error {{no match}}
  }
}

namespace dr487 { // dr487: yes
  enum E { e };
  int operator+(int, E);
  int i[4 + e]; // expected-error 2{{variable length array}}
}

namespace dr488 { // dr488: yes c++11
  template <typename T> void f(T);
  void f(int);
  void g() {
    // FIXME: It seems CWG thought this should be a SFINAE failure prior to
    // allowing local types as template arguments. In C++98, we should either
    // allow local types as template arguments or treat this as a SFINAE
    enum E { e };
    f(e);
#if __cplusplus < 201103L
#endif
  }
}

// dr489: na

namespace dr490 { // dr490: yes
  template<typename T> struct X {};

  struct A {
    typedef int T;
    struct K {}; // expected-note {{declared}}

    int f(T);
    int g(T);
    int h(X<T>);
    int X<T>::*i(); // expected-note {{previous}}
    int K::*j();

    template<typename T> T k();

    operator X<T>();
  };

  struct B {
    typedef char T;
    typedef int U;
    friend int A::f(T);
    friend int A::g(U);
    friend int A::h(X<T>);

    // FIXME: Per this DR, these two are valid! That is another defect
    // (no number yet...) which will eventually supersede this one.
    friend int X<T>::*A::i(); // expected-error {{return type}}
    friend int K::*A::j(); // expected-error {{undeclared identifier 'K'; did you mean 'A::K'?}}

    // ok, lookup finds B::T, not A::T, so return type matches
    friend char A::k<T>();
    friend int A::k<U>();

    // A conversion-type-id in a conversion-function-id is always looked up in
    // the class of the conversion function first.
    friend A::operator X<T>();
  };
}

namespace dr491 { // dr491: dup 413
  struct A {} a, b[3] = { a, {} };
  A c[2] = { a, {}, b[1] }; // expected-error {{excess elements}}
}

// dr492 FIXME write a codegen test

namespace dr493 { // dr493: dup 976
  struct X {
    template <class T> operator const T &() const;
  };
  void f() {
    if (X()) {
    }
  }
}

namespace dr494 { // dr494: dup 372
  class A {
    class B {};
    friend class C;
  };
  class C : A::B {
    A::B x;
    class D : A::B {
      A::B y;
    };
  };
}

namespace dr495 { // dr495: 3.5
  template<typename T>
  struct S {
    operator int() { return T::error; }
    template<typename U> operator U();
  };
  S<int> s;
  long n = s;

  template<typename T>
  struct S2 {
    template<typename U> operator U();
    operator int() { return T::error; }
  };
  S2<int> s2;
  long n2 = s2;
}

namespace dr496 { // dr496: sup 2094
  struct A { int n; };
  struct B { volatile int n; };
  int check1[ __is_trivially_copyable(const int) ? 1 : -1];
  // This checks the dr2094 behavior, not dr496
  int check2[ __is_trivially_copyable(volatile int) ? 1 : -1];
  int check3[ __is_trivially_constructible(A, const A&) ? 1 : -1];
  int check4[ __is_trivially_constructible(B, const B&) ? 1 : -1];
  int check5[ __is_trivially_assignable(A, const A&) ? 1 : -1];
  int check6[ __is_trivially_assignable(B, const B&) ? 1 : -1];
}

namespace dr497 { // dr497: sup 253
  void before() {
    struct S {
      mutable int i;
    };
    const S cs;
    int S::*pm = &S::i;
    cs.*pm = 88; // expected-error {{not assignable}}
  }

  void after() {
    struct S {
      S() : i(0) {}
      mutable int i;
    };
    const S cs;
    int S::*pm = &S::i;
    cs.*pm = 88; // expected-error {{not assignable}}
  }
}

namespace dr499 { // dr499: yes
  extern char str[];
  void f() { throw str; }
}


try {
      for (int i = 0; i < numAdditions; i++) {
        futures.push_back(std::async(
          std::launch::async, addListener, std::ref(listenerFlags), i));
      }
      for (auto& r : readies) {
        r.get_future().wait();
      }

      go.set_value();

      for (auto& future : futures) {
        tokens.push_back(future.get());
      }
    } catch (...) {
      go.set_value();
      throw;
    }

/*=============================================================================

  Library: CppMicroServices

  file at the top-level directory of this distribution and at
  http://example.com .

=============================================================================*/

#include "cppmicroservices/Bundle.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/BundleEvent.h"
#include "cppmicroservices/Constants.h"
#include "cppmicroservices/Framework.h"
#include "cppmicroservices/FrameworkEvent.h"
#include "cppmicroservices/FrameworkFactory.h"
#include "cppmicroservices/ServiceEvent.h"

#include "TestUtils.h"
#include "TestingConfig.h"
#include "TestingMacros.h"

#include <array>
#include <bitset>
#include <future>

using namespace cppmicroservices;

namespace {
enum ListenerType : short
{
  CALLBACK1 = 0,
  CALLBACK2,
  CALLBACK3,
  FUNCTOR,
  MEMFN1,
  MEMFN2,
  LAMBDA1,
  LAMBDA2,
};

int count = 0;
const int BITFIELD_LEN = 8;
std::bitset<BITFIELD_LEN> bitfield(0x0);

void callback_function_1(const FrameworkEvent&)
{
  bitfield.set(CALLBACK1);
  ++count;
}

void callback_function_2(const FrameworkEvent&)
{
  bitfield.set(CALLBACK2);
  ++count;
}

void callback_function_3(int val, const FrameworkEvent&)
{
  US_UNUSED(val);
  bitfield.set(CALLBACK3);
  ++count;
}

class CallbackFunctor
{
public:
  void operator()(const FrameworkEvent&)
  {
    bitfield.set(FUNCTOR);
    ++count;
  }
};

class Listener
{
public:
  void memfn1(const FrameworkEvent&)
  {
    bitfield.set(MEMFN1);
    ++count;
  }

  void memfn2(const FrameworkEvent&)
  {
    bitfield.set(MEMFN2);
    ++count;
  }
};

void testMultipleListeners()
{
  auto lambda1 = [](const FrameworkEvent&) {
    bitfield.set(LAMBDA1);
    ++count;
  };
  auto lambda2 = [](const FrameworkEvent&) {
    bitfield.set(LAMBDA2);
    ++count;
  };
  CallbackFunctor cb;
  Listener l1;
  Listener l2;

  auto f = FrameworkFactory().NewFramework();

  // 1. Add all listeners
  f.Init();
  BundleContext fCtx{ f.GetBundleContext() };
  fCtx.AddFrameworkListener(callback_function_1);
  fCtx.AddFrameworkListener(&callback_function_2);
  fCtx.AddFrameworkListener(&l1, &Listener::memfn1);
  fCtx.AddFrameworkListener(&l2, &Listener::memfn2);
  fCtx.AddFrameworkListener(cb);
  fCtx.AddFrameworkListener(lambda1);
  fCtx.AddFrameworkListener(lambda2);
  fCtx.AddFrameworkListener(CallbackFunctor());
  fCtx.AddFrameworkListener(
    std::bind(callback_function_3, 42, std::placeholders::_1));
  f.Start(); // generate framework event (started)
  US_TEST_CONDITION(bitfield.all(), "Test if all the listeners are triggered.");
  US_TEST_CONDITION(count == 9, "Test the listeners count.")
  f.Stop();
  f.WaitForStop(std::chrono::milliseconds::zero());
  US_TEST_OUTPUT(<< "-- End of testing addition of multiple listeners"
                 << "\n\n");

  // 2. Add all listeners and try removing listeners using their name
  // This removal using the names is deprecated and will be removed in the next major release.
  f.Init();
  fCtx = f.GetBundleContext();
  bitfield.reset();
  count = 0;
  // Add listeners of each variety
  fCtx.AddFrameworkListener(callback_function_1);
  fCtx.AddFrameworkListener(&callback_function_2);
  fCtx.AddFrameworkListener(&l1, &Listener::memfn1);
  fCtx.AddFrameworkListener(&l2, &Listener::memfn2);
  fCtx.AddFrameworkListener(cb);
  fCtx.AddFrameworkListener(lambda1);
  fCtx.AddFrameworkListener(lambda2);
  fCtx.AddFrameworkListener(CallbackFunctor());
  auto bind1 = std::bind(callback_function_3, 42, std::placeholders::_1);
  fCtx.AddFrameworkListener(bind1);
  // Remove listeners
  fCtx.RemoveFrameworkListener(callback_function_1);
  fCtx.RemoveFrameworkListener(&callback_function_2);
  fCtx.RemoveFrameworkListener(&l1, &Listener::memfn1);
  fCtx.RemoveFrameworkListener(&l2, &Listener::memfn2);
  fCtx.RemoveFrameworkListener(cb);
  fCtx.RemoveFrameworkListener(bind1);
  fCtx.RemoveFrameworkListener(lambda1);
  fCtx.RemoveFrameworkListener(lambda2);
  // Remove few listeners one more time.
  fCtx.RemoveFrameworkListener(callback_function_1);
  fCtx.RemoveFrameworkListener(&callback_function_2);
  fCtx.RemoveFrameworkListener(&l1, &Listener::memfn1);
  fCtx.RemoveFrameworkListener(&l2, &Listener::memfn2);
  fCtx.RemoveFrameworkListener(cb);
  fCtx.RemoveFrameworkListener(bind1);
  f.Start(); // generate framework event (started)
  US_TEST_CONDITION(bitfield.none(),
                    "Test if none of the listeners are registered.");
  US_TEST_CONDITION(count == 0, "Test the listeners count.")
  f.Stop();
  f.WaitForStop(std::chrono::milliseconds::zero());
  US_TEST_OUTPUT(
    << "-- End of testing removing listeners using the name of the callable"
    << "\n\n");

  // 3. Add all listeners and remove them using tokens
  f.Init();
  fCtx = f.GetBundleContext();
  auto token1 = fCtx.AddFrameworkListener(callback_function_1);
  auto token2 = fCtx.AddFrameworkListener(&callback_function_2);
  auto token3 = fCtx.AddFrameworkListener(&l1, &Listener::memfn1);
  auto token4 = fCtx.AddFrameworkListener(&l1, &Listener::memfn2);
  auto token5 = fCtx.AddFrameworkListener(&l2, &Listener::memfn1);
  auto token6 = fCtx.AddFrameworkListener(&l2, &Listener::memfn2);
  auto token7 = fCtx.AddFrameworkListener(cb);
  auto token8 = fCtx.AddFrameworkListener(lambda1);
  auto token9 = fCtx.AddFrameworkListener(lambda2);
  auto token10 = fCtx.AddFrameworkListener(CallbackFunctor());
  auto token11 = fCtx.AddFrameworkListener(bind1);
  // Remove all added listeners using tokens.
  fCtx.RemoveListener(std::move(token1));
  fCtx.RemoveListener(std::move(token2));
  fCtx.RemoveListener(std::move(token3));
  fCtx.RemoveListener(std::move(token4));
  fCtx.RemoveListener(std::move(token5));
  fCtx.RemoveListener(std::move(token6));
  fCtx.RemoveListener(std::move(token7));
  fCtx.RemoveListener(std::move(token8));
  fCtx.RemoveListener(std::move(token9));
  fCtx.RemoveListener(std::move(token10));
  fCtx.RemoveListener(std::move(token11));
  // Remove all added listeners again using the tokens. These should all be no-op.
  fCtx.RemoveListener(std::move(token1));
  fCtx.RemoveListener(std::move(token2));
  fCtx.RemoveListener(std::move(token3));
  fCtx.RemoveListener(std::move(token4));
  fCtx.RemoveListener(std::move(token5));
  fCtx.RemoveListener(std::move(token6));
  fCtx.RemoveListener(std::move(token7));
  fCtx.RemoveListener(std::move(token8));
  fCtx.RemoveListener(std::move(token9));
  fCtx.RemoveListener(std::move(token10));
  fCtx.RemoveListener(std::move(token11));
  // This should result in no output because all the listeners were successfully removed
  f.Start(); // generate framework event (started)
  US_TEST_CONDITION(bitfield.none(),
                    "Test if none of the listeners are registered.");
  US_TEST_CONDITION(count == 0, "Test the listeners count.")
  f.Stop();
  f.WaitForStop(std::chrono::milliseconds::zero());
  US_TEST_OUTPUT(
    << "-- End of testing addition and removing listeners using tokens"
    << "\n\n");

  // 4. Test the move ability
  f.Init();
  fCtx = f.GetBundleContext();
  token1 = fCtx.AddFrameworkListener(callback_function_1);
  US_TEST_CONDITION(token1, "Check validity of a token1");
  token2 = fCtx.AddFrameworkListener(&callback_function_2);
  token3 = fCtx.AddFrameworkListener(&l1, &Listener::memfn1);
  token3 = fCtx.AddFrameworkListener(&l2, &Listener::memfn2);
  token4 = std::move(token1); // move assignment
  US_TEST_CONDITION(!token1, "Check invalidity of a moved-from token1");
  auto token2_(std::move(token2)); // move construction
  US_TEST_CONDITION(!token2, "Check invalidity of a moved-from token2");
  ListenerToken emptytoken; // default construction
  US_TEST_CONDITION(!emptytoken,
                    "Check invalidity of a newly constructed token");
  fCtx.RemoveListener(std::move(token4));
  fCtx.RemoveListener(std::move(token2_));
  fCtx.RemoveListener(std::move(emptytoken)); // This should do nothing.
  f.Start(); // generate framework event (started)
  US_TEST_CONDITION(bitfield.to_string() == "00110000",
                    "Test if only member functions 1 & 2 are registered.");
  US_TEST_CONDITION(count == 2, "Test the listeners count.")
  f.Stop();
  f.WaitForStop(std::chrono::milliseconds::zero());
  US_TEST_OUTPUT(<< "-- End of testing move ability"
                 << "\n\n");
}

void testListenerTypes()
{
  class TestListener
  {
  public:
    TestListener()
      : service_count(0)
      , bundle_count(0)
      , framework_count(0)
    {}

    void serviceChanged(const ServiceEvent& evt)
    {
      US_TEST_OUTPUT(<< "ServiceEvent: " << evt);
      ++service_count;
    }
    void bundleChanged(const BundleEvent& evt)
    {
      US_TEST_OUTPUT(<< "BundleEvent: " << evt);
      ++bundle_count;
    }
    void frameworkChanged(const FrameworkEvent& evt)
    {
      US_TEST_OUTPUT(<< "FrameworkEvent: " << evt);
      ++framework_count;
    }

    std::vector<ListenerToken> tokens;
    int service_count;
    int bundle_count;
    int framework_count;
  };

  auto f = FrameworkFactory().NewFramework();

  f.Init();
  auto fCtx = f.GetBundleContext();
  TestListener tListen;

  tListen.tokens.push_back(
    fCtx.AddServiceListener(&tListen, &TestListener::serviceChanged));
  tListen.tokens.push_back(
    fCtx.AddServiceListener(&tListen, &TestListener::serviceChanged));
  tListen.tokens.push_back(
    fCtx.AddBundleListener(&tListen, &TestListener::bundleChanged));
  tListen.tokens.push_back(
    fCtx.AddBundleListener(&tListen, &TestListener::bundleChanged));
  tListen.tokens.push_back(
    fCtx.AddFrameworkListener(&tListen, &TestListener::frameworkChanged));
  tListen.tokens.push_back(
    fCtx.AddFrameworkListener(&tListen, &TestListener::frameworkChanged));

  fCtx.RemoveListener(std::move(tListen.tokens[0]));
  fCtx.RemoveListener(std::move(tListen.tokens[2]));
  fCtx.RemoveListener(std::move(tListen.tokens[4]));
  f.Start();

  auto bundleA = testing::InstallLib(fCtx, "TestBundleA");
  US_TEST_CONDITION_REQUIRED(bundleA, "Test for existing bundle TestBundleA")
  bundleA.Start();

  US_TEST_CONDITION(tListen.service_count == 1,
                    "Test for number of times service listeners got triggered")
  US_TEST_CONDITION(tListen.bundle_count > 0,
                    "Test for number of times bundle listeners got triggered")
  US_TEST_CONDITION(
    tListen.framework_count == 1,
    "Test for number of times framework listeners got triggered")

  bundleA.Stop();
  f.Stop();
  f.WaitForStop(std::chrono::milliseconds::zero());
}

#ifdef US_ENABLE_THREADING_SUPPORT
// Test the addition of thousand listeners asynchronously.
void testConcurrentAdd()
{
  FrameworkFactory factory;
  auto framework = factory.NewFramework();
  framework.Init();
  BundleContext fCtx = framework.GetBundleContext();

  const int numAdditions = 1001;
  std::vector<ListenerToken> tokens;
  std::vector<std::future<ListenerToken>> futures;
  int count = 0;

  auto addListener = [&fCtx, &count]() -> ListenerToken {
    auto listener = [&count](const FrameworkEvent&) { ++count; };
    auto token = fCtx.AddFrameworkListener(listener);
    return token;
  };

  for (int i = 0; i < numAdditions; i++) {
    futures.push_back(std::async(std::launch::async, addListener));
  }
  for (auto& future : futures) {
    tokens.push_back(future.get());
  }
  for (auto& token : tokens) {
    fCtx.RemoveListener(std::move(token));
  }

  framework.Start();
  US_TEST_CONDITION(
    count == 0, "Testing concurrent listener addition and sequential removal.")
  framework.Stop();
  framework.WaitForStop(std::chrono::seconds(0));
}

// NOTE: These should be replaced with template lambdas and moved inside
// testConcurrectAddRemove() when we support only C++14 and beyond.
template<typename ListenerType>
ListenerToken AddListener(BundleContext&, ListenerType)
{
  return ListenerToken();
}

template<>
ListenerToken AddListener(BundleContext& fCtx, FrameworkListener listener)
{
  return fCtx.AddFrameworkListener(listener);
}

template<>
ListenerToken AddListener(BundleContext& fCtx, ServiceListener listener)
{
  return fCtx.AddServiceListener(listener);
}

template<>
ListenerToken AddListener(BundleContext& fCtx, BundleListener listener)
{
  return fCtx.AddBundleListener(listener);
}

// Test true concurrent addition and removal of listeners.
// This is a better simulation of what we want to test - that only the API
// calls that add listeners or remove listeners are executed at the same time,
// and not other boilerplate code.
template<typename ListenerType, typename EventType>
void testConcurrentAddRemove(std::string listenerStr)
{
  FrameworkFactory factory;
  auto framework = factory.NewFramework();
  framework.Init();
  BundleContext fCtx = framework.GetBundleContext();

  const int numAdditions = 50;
  const int numRemovals = 30;
  std::vector<uint8_t> listenerFlags(numAdditions, 0);
  std::vector<ListenerToken> tokens;

  // Test concurrent addition
  // All threads wait at the 'ready.wait()' point inside the lambdas,
  // until the time when the promise 'go' is set by the main thread.
  {
    std::vector<std::future<ListenerToken>> futures;
    std::promise<void> go;
    std::shared_future<void> ready(go.get_future());
    std::vector<std::promise<void>> readies(numAdditions);

    auto addListener = [&fCtx, &readies, ready](std::vector<uint8_t>& flags,
                                                int i) -> ListenerToken {
      auto listener = [&flags, i](const EventType&) { flags[i] = 1; };
      readies[i].set_value();
      ready.wait();
      auto token = AddListener<ListenerType>(fCtx, listener);
      return token;
    };

  }

  // Test concurrent removal
  // All threads wait at the 'ready.wait()' point inside the lambdas,
  // until the time when the promise 'go' is set by the main thread.
  {
    std::vector<std::future<void>> futures;
    std::promise<void> go;
    std::shared_future<void> ready(go.get_future());
    std::vector<std::promise<void>> readies(numRemovals);

    // Using ListenerToken& because of VS2013 compiler bug
    auto removeListener = [&fCtx, &readies, ready](int i,
                                                   ListenerToken& token) {
      readies[i].set_value();
      ready.wait();
      fCtx.RemoveListener(std::move(token));
    };

    try {
      for (int i = 0; i < numRemovals; i++) {
        futures.push_back(std::async(
          std::launch::async, removeListener, i, std::ref(tokens[i])));
      }
      for (auto& r : readies) {
        r.get_future().wait();
      }

      go.set_value();

      for (auto& future : futures) {
        future.get();
      }
    } catch (...) {
      go.set_value();
      throw;
    }
  }

  framework.Start();
  auto bundleA = testing::InstallLib(fCtx, "TestBundleA");
  bundleA.Start();

  auto countOnes = std::count(listenerFlags.begin(), listenerFlags.end(), 1);
  US_TEST_OUTPUT(<< "");
  std::string message("Testing concurrent " + listenerStr +
                      " addition and removal");
  US_TEST_CONDITION(countOnes == numAdditions - numRemovals, message)
  bundleA.Stop();
  framework.Stop();
  framework.WaitForStop(std::chrono::seconds(0));
}
#endif // US_ENABLE_THREADING_SUPPORT
}

int MultipleListenersTest(int /*argc*/, char* /*argv*/ [])
{
  US_TEST_BEGIN("MultipleListenersTest");
  testMultipleListeners();
  testListenerTypes();

#ifdef US_ENABLE_THREADING_SUPPORT
  testConcurrentAdd();
  // Test all the three types of listeners.
  testConcurrentAddRemove<FrameworkListener, FrameworkEvent>(
    "framework listener");
  testConcurrentAddRemove<BundleListener, BundleEvent>("bundle listener");
  testConcurrentAddRemove<ServiceListener, ServiceEvent>("service listener");
#endif

  US_TEST_END()
}


try
							{
								obj->SerializeUserVars(*this);
								obj->Serialize(*this);
							}
							catch (CRecoverableError &err)
							{
								// In case something in here throws an error, let's continue and deal with it later.
								Printf(TEXTCOLOR_RED "'%s'\n while restoring %s", err.GetMessage(), obj ? obj->GetClass()->TypeName.GetChars() : "invalid object");
								mErrors++;
							}

/*
** serializer.cpp
** Savegame wrapper around RapidJSON
**
**---------------------------------------------------------------------------
** All rights reserved.
**
**---------------------------------------------------------------------------
**
*/

#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0	// disable this insanity which is bound to make the code break over time.
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag

#include "rapidjson/rapidjson.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/document.h"
#include "serializer.h"
#include "r_data/colormaps.h"
#include "r_data/r_interpolate.h"
#include "r_defs.h"
#include "r_state.h"
#include "p_lnspec.h"
#include "i_system.h"
#include "w_wad.h"
#include "p_terrain.h"
#include "c_dispatch.h"
#include "p_setup.h"
#include "p_conversation.h"
#include "dsectoreffect.h"
#include "d_player.h"
#include "r_data/r_interpolate.h"
#include "g_shared/a_sharedglobal.h"
#include "po_man.h"
#include "v_font.h"
#include "w_zip.h"
#include "doomerrors.h"
#include "v_text.h"
#include "cmdlib.h"

char nulspace[1024 * 1024 * 4];
bool save_full = false;	// for testing. Should be removed afterward.

int utf8_encode(int32_t codepoint, char *buffer, int *size)
{
	if (codepoint < 0)
		return -1;
	else if (codepoint < 0x80)
	{
		buffer[0] = (char)codepoint;
		*size = 1;
	}
	else if (codepoint < 0x800)
	{
		buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6);
		buffer[1] = 0x80 + ((codepoint & 0x03F));
		*size = 2;
	}
	else if (codepoint < 0x10000)
	{
		buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12);
		buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6);
		buffer[2] = 0x80 + ((codepoint & 0x003F));
		*size = 3;
	}
	else if (codepoint <= 0x10FFFF)
	{
		buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18);
		buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12);
		buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6);
		buffer[3] = 0x80 + ((codepoint & 0x00003F));
		*size = 4;
	}
	else
		return -1;

	return 0;
}

int utf8_decode(const char *src, int *size)
{
	int c = src[0] & 255;
	int r;

	*size = 1;
	if ((c & 0x80) == 0)
	{
		return c;
	}

	int c1 = src[1] & 255;

	if ((c & 0xE0) == 0xC0)
	{
		r = ((c & 0x1F) << 6) | c1;
		if (r >= 128)
		{
			*size = 2;
			return r;
		}
		return -1;
	}

	int c2 = src[2] & 255;

	if ((c & 0xF0) == 0xE0)
	{
		r = ((c & 0x0F) << 12) | (c1 << 6) | c2;
		if (r >= 2048 && (r < 55296 || r > 57343))
		{
			*size = 3;
			return r;
		}
		return -1;
	}

	int c3 = src[3] & 255;

	if ((c & 0xF8) == 0xF0)
	{
		r = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
		if (r >= 65536 && r <= 1114111)
		{
			*size = 4;
			return r;
		}
	}
	return -1;
}

static TArray<char> out;
static const char *StringToUnicode(const char *cc, int size = -1)
{
	int ch;
	const char *c = cc;
	int count = 0;
	int count1 = 0;
	out.Clear();
	while ((ch = (*c++) & 255))
	{
		count1++;
		if (ch >= 128)
		{
			if (ch < 0x800) count += 2;
			else count += 3;
			// The source cannot contain 4-byte chars.
		}
		else count++;
		if (count1 == size && size > 0) break;
	}
	if (count == count1) return cc;	// string is pure ASCII.
	// we need to convert
	out.Resize(count + 1);
	out.Last() = 0;
	c = cc;
	int i = 0;
	while ((ch = (*c++) & 255))
	{
		utf8_encode(ch, &out[i], &count1);
		i += count1;
	}
	return &out[0];
}

static const char *UnicodeToString(const char *cc)
{
	out.Resize((unsigned)strlen(cc) + 1);
	int ndx = 0;
	while (*cc != 0)
	{
		int size;
		int c = utf8_decode(cc, &size);
		if (c < 0 || c > 255) c = '?';
		out[ndx++] = c;
		cc += size;
	}
	out[ndx] = 0;
	return &out[0];
}

struct FJSONObject
{
	rapidjson::Value *mObject;
	rapidjson::Value::MemberIterator mIterator;
	int mIndex;

	FJSONObject(rapidjson::Value *v)
	{
		mObject = v;
		if (v->IsObject()) mIterator = v->MemberBegin();
		else if (v->IsArray())
		{
			mIndex = 0;
		}
	}
};

// some wrapper stuff to keep the RapidJSON dependencies out of the global headers.
// FSerializer should not expose any of this.

struct FWriter
{
	typedef rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<> > Writer;
	typedef rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<> > PrettyWriter;

	Writer *mWriter1;
	PrettyWriter *mWriter2;
	TArray<bool> mInObject;
	rapidjson::StringBuffer mOutString;
	TArray<DObject *> mDObjects;
	TMap<DObject *, int> mObjectMap;

	FWriter(bool pretty)
	{
		if (!pretty)
		{
			mWriter1 = new Writer(mOutString);
			mWriter2 = nullptr;
		}
		else
		{
			mWriter1 = nullptr;
			mWriter2 = new PrettyWriter(mOutString);
		}
	}

	~FWriter()
	{
		if (mWriter1) delete mWriter1;
		if (mWriter2) delete mWriter2;
	}

	bool inObject() const
	{
		return mInObject.Size() > 0 && mInObject.Last();
	}

	void StartObject()
	{
		if (mWriter1) mWriter1->StartObject();
		else if (mWriter2) mWriter2->StartObject();
	}

	void EndObject()
	{
		if (mWriter1) mWriter1->EndObject();
		else if (mWriter2) mWriter2->EndObject();
	}

	void StartArray()
	{
		if (mWriter1) mWriter1->StartArray();
		else if (mWriter2) mWriter2->StartArray();
	}

	void EndArray()
	{
		if (mWriter1) mWriter1->EndArray();
		else if (mWriter2) mWriter2->EndArray();
	}

	void Key(const char *k)
	{
		if (mWriter1) mWriter1->Key(k);
		else if (mWriter2) mWriter2->Key(k);
	}

	void Null()
	{
		if (mWriter1) mWriter1->Null();
		else if (mWriter2) mWriter2->Null();
	}

	void String(const char *k)
	{
		k = StringToUnicode(k);
		if (mWriter1) mWriter1->String(k);
		else if (mWriter2) mWriter2->String(k);
	}

	void String(const char *k, int size)
	{
		k = StringToUnicode(k, size);
		if (mWriter1) mWriter1->String(k);
		else if (mWriter2) mWriter2->String(k);
	}

	void Bool(bool k)
	{
		if (mWriter1) mWriter1->Bool(k);
		else if (mWriter2) mWriter2->Bool(k);
	}

	void Int(int32_t k)
	{
		if (mWriter1) mWriter1->Int(k);
		else if (mWriter2) mWriter2->Int(k);
	}

	void Int64(int64_t k)
	{
		if (mWriter1) mWriter1->Int64(k);
		else if (mWriter2) mWriter2->Int64(k);
	}

	void Uint(uint32_t k)
	{
		if (mWriter1) mWriter1->Uint(k);
		else if (mWriter2) mWriter2->Uint(k);
	}

	void Uint64(int64_t k)
	{
		if (mWriter1) mWriter1->Uint64(k);
		else if (mWriter2) mWriter2->Uint64(k);
	}

	void Double(double k)
	{
		if (mWriter1)
		{
			mWriter1->Double(k);
		}
		else if (mWriter2)
		{
			mWriter2->Double(k);
		}
	}

};

struct FReader
{
	TArray<FJSONObject> mObjects;
	rapidjson::Document mDoc;
	TArray<DObject *> mDObjects;
	rapidjson::Value *mKeyValue = nullptr;
	int mPlayers[MAXPLAYERS];
	bool mObjectsRead = false;

	FReader(const char *buffer, size_t length)
	{
		rapidjson::Document doc;
		mDoc.Parse(buffer, length);
		mObjects.Push(FJSONObject(&mDoc));
		memset(mPlayers, -1, sizeof(mPlayers));
	}

	rapidjson::Value *FindKey(const char *key)
	{
		FJSONObject &obj = mObjects.Last();

		if (obj.mObject->IsObject())
		{
			if (key == nullptr)
			{
				// we are performing an iteration of the object through GetKey.
				auto p = mKeyValue;
				mKeyValue = nullptr;
				return p;
			}
			else
			{
				// Find the given key by name;
				auto it = obj.mObject->FindMember(key);
				if (it == obj.mObject->MemberEnd()) return nullptr;
				return &it->value;
			}
		}
		else if (obj.mObject->IsArray() && (unsigned)obj.mIndex < obj.mObject->Size())
		{
			return &(*obj.mObject)[obj.mIndex++];
		}
		return nullptr;
	}
};

bool FSerializer::OpenWriter(bool pretty)
{
	if (w != nullptr || r != nullptr) return false;

	mErrors = 0;
	w = new FWriter(pretty);
	BeginObject(nullptr);
	return true;
}

bool FSerializer::OpenReader(const char *buffer, size_t length)
{
	if (w != nullptr || r != nullptr) return false;

	mErrors = 0;
	r = new FReader(buffer, length);
	return true;
}

bool FSerializer::OpenReader(FCompressedBuffer *input)
{
	if (input->mSize <= 0 || input->mBuffer == nullptr) return false;
	if (w != nullptr || r != nullptr) return false;

	mErrors = 0;
	if (input->mMethod == METHOD_STORED)
	{
		r = new FReader((char*)input->mBuffer, input->mSize);
	}
	else
	{
		char *unpacked = new char[input->mSize];
		input->Decompress(unpacked);
		r = new FReader(unpacked, input->mSize);
		delete[] unpacked;
	}
	return true;
}

void FSerializer::Close()
{
	if (w != nullptr)
	{
		delete w;
		w = nullptr;
	}
	if (r != nullptr)
	{
		// we must explicitly delete all thinkers in the array which did not get linked into the thinker lists.
		// Otherwise these objects may survive a level deletion and point to incorrect data.
		for (auto &obj : r->mDObjects)
		{
			auto think = dyn_cast<DThinker>(obj);
			if (think != nullptr)
			{
				if (think->NextThinker == nullptr || think->PrevThinker == nullptr)
				{
					think->Destroy();
				}
			}
		}

		delete r;
		r = nullptr;
	}
	if (mErrors > 0)
	{
		I_Error("%d errors parsing JSON", mErrors);
	}
}

unsigned FSerializer::ArraySize()
{
	if (r != nullptr && r->mObjects.Last().mObject->IsArray())
	{
		return r->mObjects.Last().mObject->Size();
	}
	else
	{
		return 0;
	}
}

bool FSerializer::canSkip() const
{
	return isWriting() && w->inObject();
}

void FSerializer::WriteKey(const char *key)
{
	if (isWriting() && w->inObject())
	{
		assert(key != nullptr);
		if (key == nullptr)
		{
			I_Error("missing element name");
		}
		w->Key(key);
	}
}

bool FSerializer::BeginObject(const char *name)
{
	if (isWriting())
	{
		WriteKey(name);
		w->StartObject();
		w->mInObject.Push(true);
	}
	else
	{
		auto val = r->FindKey(name);
		if (val != nullptr)
		{
			assert(val->IsObject());
			if (val->IsObject())
			{
				r->mObjects.Push(FJSONObject(val));
			}
			else
			{
				Printf(TEXTCOLOR_RED "Object expected for '%s'", name);
				mErrors++;
				return false;
			}
		}
		else
		{
			return false;
		}
	}
	return true;
}

void FSerializer::EndObject()
{
	if (isWriting())
	{
		if (w->inObject())
		{
			w->EndObject();
			w->mInObject.Pop();
		}
		else
		{
			assert(false && "EndObject call not inside an object");
			I_Error("EndObject call not inside an object");
		}
	}
	else
	{
		r->mObjects.Pop();
	}
}

bool FSerializer::BeginArray(const char *name)
{
	if (isWriting())
	{
		WriteKey(name);
		w->StartArray();
		w->mInObject.Push(false);
	}
	else
	{
		auto val = r->FindKey(name);
		if (val != nullptr)
		{
			assert(val->IsArray());
			if (val->IsArray())
			{
				r->mObjects.Push(FJSONObject(val));
			}
			else
			{
				Printf(TEXTCOLOR_RED "Array expected for '%s'", name);
				mErrors++;
				return false;
			}
		}
		else
		{
			return false;
		}
	}
	return true;
}

void FSerializer::EndArray()
{
	if (isWriting())
	{
		if (!w->inObject())
		{
			w->EndArray();
			w->mInObject.Pop();
		}
		else
		{
			assert(false && "EndArray call not inside an array");
			I_Error("EndArray call not inside an array");
		}
	}
	else
	{
		r->mObjects.Pop();
	}
}

// Special handler for args (because ACS specials' arg0 needs special treatment.)

FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int special)
{
	if (isWriting())
	{
		if (w->inObject() && defargs != nullptr && !memcmp(args, defargs, 5 * sizeof(int)))
		{
			return *this;
		}

		WriteKey(key);
		w->StartArray();
		for (int i = 0; i < 5; i++)
		{
			if (i == 0 && args[i] < 0 && P_IsACSSpecial(special))
			{
				w->String(FName(ENamedName(-args[i])).GetChars());
			}
			else
			{
				w->Int(args[i]);
			}
		}
		w->EndArray();
	}
	else
	{
		auto val = r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsArray())
			{
				unsigned int cnt = MIN<unsigned>(val->Size(), 5);
				for (unsigned int i = 0; i < cnt; i++)
				{
					const rapidjson::Value &aval = (*val)[i];
					if (aval.IsInt())
					{
						args[i] = aval.GetInt();
					}
					else if (i == 0 && aval.IsString())
					{
						args[i] = -FName(UnicodeToString(aval.GetString()));
					}
					else
					{
						assert(false && "Integer expected");
						Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'", key, i);
						mErrors++;
					}
				}
			}
			else
			{
				assert(false && "array expected");
				Printf(TEXTCOLOR_RED "array expected for '%s'", key);
				mErrors++;
			}
		}
	}
	return *this;
}

// Special handler for script numbers

FSerializer &FSerializer::ScriptNum(const char *key, int &num)
{
	if (isWriting())
	{
		WriteKey(key);
		if (num < 0)
		{
			w->String(FName(ENamedName(-num)).GetChars());
		}
		else
		{
			w->Int(num);
		}
	}
	else
	{
		auto val = r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsInt())
			{
				num = val->GetInt();
			}
			else if (val->IsString())
			{
				num = -FName(UnicodeToString(val->GetString()));
			}
			else
			{
				assert(false && "Integer expected");
				Printf(TEXTCOLOR_RED "Integer expected for '%s'", key);
				mErrors++;
			}
		}
	}
	return *this;
}

FSerializer &FSerializer::Terrain(const char *key, int &terrain, int *def)
{
	if (isWriting() && def != nullptr && terrain == *def)
	{
		return *this;
	}
	FName terr = P_GetTerrainName(terrain);
	Serialize(*this, key, terr, nullptr);
	if (isReading())
	{
		terrain = P_FindTerrain(terr);
	}
	return *this;
}

FSerializer &FSerializer::Sprite(const char *key, int32_t &spritenum, int32_t *def)
{
	if (isWriting())
	{
		if (w->inObject() && def != nullptr && *def == spritenum) return *this;
		WriteKey(key);
		w->String(sprites[spritenum].name, 4);
	}
	else
	{
		auto val = r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsString())
			{
				uint32_t name = *reinterpret_cast<const uint32_t*>(UnicodeToString(val->GetString()));
				for (auto hint = NumStdSprites; hint-- != 0; )
				{
					if (sprites[hint].dwName == name)
					{
						spritenum = hint;
						break;
					}
				}
			}
		}
	}
	return *this;
}

FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr)
{
	if (isWriting())
	{
		WriteKey(key);
		if (charptr != nullptr)
			w->String(charptr);
		else
			w->Null();
	}
	else
	{
		auto val = r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsString())
			{
				charptr = UnicodeToString(val->GetString());
			}
			else
			{
				charptr = nullptr;
			}
		}
	}
	return *this;
}

FSerializer &FSerializer::AddString(const char *key, const char *charptr)
{
	if (isWriting())
	{
		WriteKey(key);
		w->String(charptr);
	}
	return *this;
}

unsigned FSerializer::GetSize(const char *group)
{
	if (isWriting()) return -1;	// we do not know this when writing.

	const rapidjson::Value &val = r->mDoc[group];
	if (!val.IsArray()) return -1;
	return val.Size();
}

// gets the key pointed to by the iterator, caches its value
// and returns the key string.

const char *FSerializer::GetKey()
{
	if (isWriting()) return nullptr;	// we do not know this when writing.
	if (!r->mObjects.Last().mObject->IsObject()) return nullptr;	// non-objects do not have keys.
	auto &it = r->mObjects.Last().mIterator;
	if (it == r->mObjects.Last().mObject->MemberEnd()) return nullptr;
	r->mKeyValue = &it->value;
	return (it++)->name.GetString();
}

// Writes out all collected objects

void FSerializer::WriteObjects()
{
	if (isWriting() && w->mDObjects.Size())
	{
		BeginArray("objects");
		// we cannot use the C++11 shorthand syntax here because the array may grow while being processed.
		for (unsigned i = 0; i < w->mDObjects.Size(); i++)
		{
			auto obj = w->mDObjects[i];

			BeginObject(nullptr);
			w->Key("classtype");
			w->String(obj->GetClass()->TypeName.GetChars());

			obj->SerializeUserVars(*this);
			obj->Serialize(*this);
			obj->CheckIfSerialized();
			EndObject();
		}
		EndArray();
	}
}

// Writes out all collected objects

void FSerializer::ReadObjects(bool hubtravel)
{
	bool founderrors = false;

	if (isReading() && BeginArray("objects"))
	{
		// Do not link any thinker that's being created here. This will be done by deserializing the thinker list later.
		try
		{
			DThinker::bSerialOverride = true;
			r->mDObjects.Resize(ArraySize());
			// First iteration: create all the objects but do nothing with them yet.
			for (unsigned i = 0; i < r->mDObjects.Size(); i++)
			{
				if (BeginObject(nullptr))
				{
					FString clsname;	// do not deserialize the class type directly so that we can print appropriate errors.
					int pindex = -1;

					Serialize(*this, "classtype", clsname, nullptr);
					PClass *cls = PClass::FindClass(clsname);
					if (cls == nullptr)
					{
						Printf("Unknown object class '%s' in savegame", clsname.GetChars());
						founderrors = true;
						r->mDObjects[i] = RUNTIME_CLASS(AActor)->CreateNew();	// make sure we got at least a valid pointer for the duration of the loading process.
						r->mDObjects[i]->Destroy();								// but we do not want to keep this around, so destroy it right away.
					}
					else
					{
						r->mDObjects[i] = cls->CreateNew();
					}
					EndObject();
				}
			}
			// Now that everything has been created and we can retrieve the pointers we can deserialize it.
			r->mObjectsRead = true;

			if (!founderrors)
			{
				// Reset to start;
				r->mObjects.Last().mIndex = 0;

				for (unsigned i = 0; i < r->mDObjects.Size(); i++)
				{
					auto obj = r->mDObjects[i];
					if (BeginObject(nullptr))
					{
						if (obj != nullptr)
						{
							int pindex = -1;

						}
						EndObject();
					}
				}
			}
			EndArray();

			DThinker::bSerialOverride = false;
			assert(!founderrors);
			if (founderrors)
			{
				Printf(TEXTCOLOR_RED "Failed to restore all objects in savegame");
				mErrors++;
			}
		}
		catch(...)
		{
			// nuke all objects we created here.
			for (auto obj : r->mDObjects)
			{
				obj->Destroy();
			}
			r->mDObjects.Clear();

			// make sure this flag gets unset, even if something in here throws an error.
			DThinker::bSerialOverride = false;
			throw;
		}
	}
}

const char *FSerializer::GetOutput(unsigned *len)
{
	if (isReading()) return nullptr;
	WriteObjects();
	EndObject();
	if (len != nullptr)
	{
		*len = (unsigned)w->mOutString.GetSize();
	}
	return w->mOutString.GetString();
}

FCompressedBuffer FSerializer::GetCompressedOutput()
{
	if (isReading()) return{ 0,0,0,0,0,nullptr };
	FCompressedBuffer buff;
	WriteObjects();
	EndObject();
	buff.mSize = (unsigned)w->mOutString.GetSize();
	buff.mZipFlags = 0;
	buff.mCRC32 = crc32(0, (const Bytef*)w->mOutString.GetString(), buff.mSize);

	uint8_t *compressbuf = new uint8_t[buff.mSize+1];

	z_stream stream;
	int err;

	stream.next_in = (Bytef *)w->mOutString.GetString();
	stream.avail_in = buff.mSize;
	stream.next_out = (Bytef*)compressbuf;
	stream.avail_out = buff.mSize;
	stream.zalloc = (alloc_func)0;
	stream.zfree = (free_func)0;
	stream.opaque = (voidpf)0;

	// create output in zip-compatible form as required by FCompressedBuffer
	err = deflateInit2(&stream, 8, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
	if (err != Z_OK)
	{
		goto error;
	}

	err = deflate(&stream, Z_FINISH);
	if (err != Z_STREAM_END)
	{
		deflateEnd(&stream);
		goto error;
	}
	buff.mCompressedSize = stream.total_out;

	err = deflateEnd(&stream);
	if (err == Z_OK)
	{
		buff.mBuffer = new char[buff.mCompressedSize];
		buff.mMethod = METHOD_DEFLATE;
		memcpy(buff.mBuffer, compressbuf, buff.mCompressedSize);
		delete[] compressbuf;
		return buff;
	}

error:
	memcpy(compressbuf, w->mOutString.GetString(), buff.mSize + 1);
	buff.mCompressedSize = buff.mSize;
	buff.mMethod = METHOD_STORED;
	return buff;
}

FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			arc.w->Bool(value);
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsBool());
			if (val->IsBool())
			{
				value = val->GetBool();
			}
			else
			{
				Printf(TEXTCOLOR_RED "boolean type expected for '%s'", key);
				arc.mErrors++;
			}
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_t *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			arc.w->Int64(value);
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsInt64());
			if (val->IsInt64())
			{
				value = val->GetInt64();
			}
			else
			{
				Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
				arc.mErrors++;
			}
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint64_t *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			arc.w->Uint64(value);
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsUint64());
			if (val->IsUint64())
			{
				value = val->GetUint64();
			}
			else
			{
				Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
				arc.mErrors++;
			}
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value, int32_t *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			arc.w->Int(value);
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsInt());
			if (val->IsInt())
			{
				value = val->GetInt();
			}
			else
			{
				Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
				arc.mErrors++;
			}
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint32_t *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			arc.w->Uint(value);
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsUint());
			if (val->IsUint())
			{
				value = val->GetUint();
			}
			else
			{
				Printf(TEXTCOLOR_RED "integer type expected for '%s'", key);
				arc.mErrors++;
			}
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, int8_t &value, int8_t *defval)
{
	int32_t vv = value;
	int32_t vvd = defval? *defval : value-1;
	Serialize(arc, key, vv, &vvd);
	value = (int8_t)vv;
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, uint8_t &value, uint8_t *defval)
{
	uint32_t vv = value;
	uint32_t vvd = defval ? *defval : value - 1;
	Serialize(arc, key, vv, &vvd);
	value = (uint8_t)vv;
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, int16_t &value, int16_t *defval)
{
	int32_t vv = value;
	int32_t vvd = defval ? *defval : value - 1;
	Serialize(arc, key, vv, &vvd);
	value = (int16_t)vv;
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value, uint16_t *defval)
{
	uint32_t vv = value;
	uint32_t vvd = defval ? *defval : value - 1;
	Serialize(arc, key, vv, &vvd);
	value = (uint16_t)vv;
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			arc.w->Double(value);
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsDouble());
			if (val->IsDouble())
			{
				value = val->GetDouble();
			}
			else
			{
				Printf(TEXTCOLOR_RED "float type expected for '%s'", key);
				arc.mErrors++;
			}
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *defval)
{
	double vv = value;
	double vvd = defval ? *defval : value - 1;
	Serialize(arc, key, vv, &vvd);
	value = (float)vv;
	return arc;
}

template<class T>
FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **defval, T *base)
{
	assert(base != nullptr);
	if (arc.isReading() || !arc.w->inObject() || defval == nullptr || value != *defval)
	{
		int64_t vv = value == nullptr ? -1 : value - base;
		Serialize(arc, key, vv, nullptr);
		value = vv < 0 ? nullptr : base + vv;
	}
	return arc;
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval)
{
	return SerializePointer(arc, key, value, defval, polyobjs);
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval)
{
	return SerializePointer<const FPolyObj>(arc, key, value, defval, polyobjs);
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval)
{
	return SerializePointer(arc, key, value, defval, sides);
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval)
{
	return SerializePointer(arc, key, value, defval, sectors);
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval)
{
	return SerializePointer<const sector_t>(arc, key, value, defval, sectors);
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval)
{
	return SerializePointer(arc, key, value, defval, players);
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval)
{
	return SerializePointer(arc, key, value, defval, lines);
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval)
{
	return SerializePointer(arc, key, value, defval, vertexes);
}

FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			if (!value.Exists())
			{
				arc.WriteKey(key);
				arc.w->Null();
				return arc;
			}
			if (value.isNull())
			{
				// save 'no texture' in a more space saving way
				arc.WriteKey(key);
				arc.w->Int(0);
				return arc;
			}
			FTextureID chk = value;
			if (chk.GetIndex() >= TexMan.NumTextures()) chk.SetNull();
			FTexture *pic = TexMan[chk];
			const char *name;

			if (Wads.GetLinkedTexture(pic->SourceLump) == pic)
			{
				name = Wads.GetLumpFullName(pic->SourceLump);
			}
			else
			{
				name = pic->Name;
			}
			arc.WriteKey(key);
			arc.w->StartArray();
			arc.w->String(name);
			arc.w->Int(pic->UseType);
			arc.w->EndArray();
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsArray())
			{
				const rapidjson::Value &nameval = (*val)[0];
				const rapidjson::Value &typeval = (*val)[1];
				assert(nameval.IsString() && typeval.IsInt());
				if (nameval.IsString() && typeval.IsInt())
				{
					value = TexMan.GetTexture(UnicodeToString(nameval.GetString()), typeval.GetInt());
				}
				else
				{
					Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key);
					value.SetNull();
					arc.mErrors++;
				}
			}
			else if (val->IsNull())
			{
				value.SetInvalid();
			}
			else if (val->IsInt() && val->GetInt() == 0)
			{
				value.SetNull();
			}
			else
			{
				assert(false && "not a texture");
				Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key);
				value.SetNull();
				arc.mErrors++;
			}
		}
	}
	return arc;
}

// This never uses defval and instead uses 'null' as default
// because object pointers cannot be safely defaulted to anything else.

FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/, bool *retcode)
{
	if (retcode) *retcode = true;
	if (arc.isWriting())
	{
		if (value != nullptr)
		{
			int ndx;
			if (value == WP_NOCHANGE)
			{
				ndx = -1;
			}
			else if (value->ObjectFlags & (OF_EuthanizeMe | OF_Transient))
			{
				return arc;
			}
			else
			{
				int *pndx = arc.w->mObjectMap.CheckKey(value);
				if (pndx != nullptr)
				{
					ndx = *pndx;
				}
				else
				{
					ndx = arc.w->mDObjects.Push(value);
					arc.w->mObjectMap[value] = ndx;
				}
			}
			Serialize(arc, key, ndx, nullptr);
		}
		else if (!arc.w->inObject())
		{
			arc.w->Null();
		}
	}
	else
	{
		if (!arc.r->mObjectsRead)
		{
			// If you want to read objects, you MUST call ReadObjects first, even if there's only nullptr's.
			assert(false && "Attempt to read object reference without calling ReadObjects first");
			I_Error("Attempt to read object reference without calling ReadObjects first");
		}
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsNull())
			{
				value = nullptr;
				return arc;
			}
			else if (val->IsInt())
			{
				int index = val->GetInt();
				if (index == -1)
				{
					value = WP_NOCHANGE;
				}
				else
				{
					assert(index >= 0 && index < (int)arc.r->mDObjects.Size());
					if (index >= 0 && index < (int)arc.r->mDObjects.Size())
					{
						value = arc.r->mDObjects[index];
					}
					else
					{
						assert(false && "invalid object reference");
						Printf(TEXTCOLOR_RED "Invalid object reference for '%s'", key);
						value = nullptr;
						arc.mErrors++;
						if (retcode) *retcode = false;
					}
				}
				return arc;
			}
		}
		if (!retcode)
		{
			value = nullptr;
		}
		else
		{
			*retcode = false;
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			arc.w->String(value.GetChars());
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsString());
			if (val->IsString())
			{
				value = UnicodeToString(val->GetString());
			}
			else
			{
				Printf(TEXTCOLOR_RED "String expected for '%s'", key);
				arc.mErrors++;
				value = NAME_None;
			}
		}
	}
	return arc;
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm, FDynamicColormap **def)
{
	if (arc.isWriting())
	{
		if (arc.w->inObject() && def != nullptr && cm->Color == (*def)->Color && cm->Fade == (*def)->Fade && cm->Desaturate == (*def)->Desaturate)
		{
			return arc;
		}

		arc.WriteKey(key);
		arc.w->StartArray();
		arc.w->Uint(cm->Color);
		arc.w->Uint(cm->Fade);
		arc.w->Uint(cm->Desaturate);
		arc.w->EndArray();
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsArray())
			{
				const rapidjson::Value &colorval = (*val)[0];
				const rapidjson::Value &fadeval = (*val)[1];
				const rapidjson::Value &desatval = (*val)[2];
				if (colorval.IsUint() && fadeval.IsUint() && desatval.IsUint())
				{
					cm = GetSpecialLights(colorval.GetUint(), fadeval.GetUint(), desatval.GetUint());
					return arc;
				}
			}
			assert(false && "not a colormap");
			Printf(TEXTCOLOR_RED "object does not represent a colormap for '%s'", key);
			cm = &NormalLight;
		}
	}
	return arc;
}

FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || sid != *def)
		{
			arc.WriteKey(key);
			const char *sn = (const char*)sid;
			if (sn != nullptr) arc.w->String(sn);
			else arc.w->Null();
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsString() || val->IsNull());
			if (val->IsString())
			{
				sid = UnicodeToString(val->GetString());
			}
			else if (val->IsNull())
			{
				sid = 0;
			}
			else
			{
				Printf(TEXTCOLOR_RED "string type expected for '%s'", key);
				sid = 0;
				arc.mErrors++;
			}
		}
	}
	return arc;

}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || clst != *def)
		{
			arc.WriteKey(key);
			if (clst == nullptr)
			{
				arc.w->Null();
			}
			else
			{
				arc.w->String(clst->TypeName.GetChars());
			}
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsString() || val->IsNull());
			if (val->IsString())
			{
				clst = PClass::FindActor(UnicodeToString(val->GetString()));
			}
			else if (val->IsNull())
			{
				clst = nullptr;
			}
			else
			{
				Printf(TEXTCOLOR_RED "string type expected for '%s'", key);
				clst = nullptr;
				arc.mErrors++;
			}
		}
	}
	return arc;

}

// almost, but not quite the same as the above.

template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&clst, PClass **def)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || clst != *def)
		{
			arc.WriteKey(key);
			if (clst == nullptr)
			{
				arc.w->Null();
			}
			else
			{
				arc.w->String(clst->TypeName.GetChars());
			}
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsString())
			{
				clst = PClass::FindClass(UnicodeToString(val->GetString()));
			}
			else if (val->IsNull())
			{
				clst = nullptr;
			}
			else
			{
				Printf(TEXTCOLOR_RED "string type expected for '%s'", key);
				clst = nullptr;
				arc.mErrors++;
			}
		}
	}
	return arc;

}

FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def, bool *retcode)
{
	if (retcode) *retcode = false;
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || state != *def)
		{
			if (retcode) *retcode = true;
			arc.WriteKey(key);
			if (state == nullptr)
			{
				arc.w->Null();
			}
			else
			{
				PClassActor *info = FState::StaticFindStateOwner(state);

				if (info != NULL)
				{
					arc.w->StartArray();
					arc.w->String(info->TypeName.GetChars());
					arc.w->Uint((uint32_t)(state - info->OwnedStates));
					arc.w->EndArray();
				}
				else
				{
					arc.w->Null();
				}
			}
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			if (val->IsNull())
			{
				if (retcode) *retcode = true;
				state = nullptr;
			}
			else if (val->IsArray())
			{
				if (retcode) *retcode = true;
				const rapidjson::Value &cls = (*val)[0];
				const rapidjson::Value &ndx = (*val)[1];

				state = nullptr;
				assert(cls.IsString() && ndx.IsUint());
				if (cls.IsString() && ndx.IsUint())
				{
					PClassActor *clas = PClass::FindActor(UnicodeToString(cls.GetString()));
					if (clas && ndx.GetUint() < (unsigned)clas->NumOwnedStates)
					{
						state = clas->OwnedStates + ndx.GetUint();
					}
					else
					{
						// this can actually happen by changing the DECORATE so treat it as a warning, not an error.
						state = nullptr;
						Printf(TEXTCOLOR_ORANGE "Invalid state '%s+%d' for '%s'", cls.GetString(), ndx.GetInt(), key);
					}
				}
				else
				{
					assert(false && "not a state");
					Printf(TEXTCOLOR_RED "data does not represent a state for '%s'", key);
					arc.mErrors++;
				}
			}
			else if (!retcode)
			{
				assert(false && "not an array");
				Printf(TEXTCOLOR_RED "array type expected for '%s'", key);
				arc.mErrors++;
			}
		}
	}
	return arc;

}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || node != *def)
		{
			arc.WriteKey(key);
			if (node == nullptr)
			{
				arc.w->Null();
			}
			else
			{
				arc.w->Uint(node->ThisNodeNum);
			}
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsUint() || val->IsNull());
			if (val->IsNull())
			{
				node = nullptr;
			}
			else if (val->IsUint())
			{
				if (val->GetUint() >= StrifeDialogues.Size())
				{
					node = nullptr;
				}
				else
				{
					node = StrifeDialogues[val->GetUint()];
				}
			}
			else
			{
				Printf(TEXTCOLOR_RED "integer expected for '%s'", key);
				arc.mErrors++;
				node = nullptr;
			}
		}
	}
	return arc;

}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || pstr != *def)
		{
			arc.WriteKey(key);
			if (pstr == nullptr)
			{
				arc.w->Null();
			}
			else
			{
				arc.w->String(pstr->GetChars());
			}
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsNull() || val->IsString());
			if (val->IsNull())
			{
				pstr = nullptr;
			}
			else if (val->IsString())
			{
				pstr = AActor::mStringPropertyData.Alloc(UnicodeToString(val->GetString()));
			}
			else
			{
				Printf(TEXTCOLOR_RED "string expected for '%s'", key);
				pstr = nullptr;
				arc.mErrors++;
			}
		}
	}
	return arc;

}

FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString *def)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || pstr.Compare(*def) != 0)
		{
			arc.WriteKey(key);
			arc.w->String(pstr.GetChars());
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsNull() || val->IsString());
			if (val->IsNull())
			{
				pstr = "";
			}
			else if (val->IsString())
			{
				pstr = UnicodeToString(val->GetString());
			}
			else
			{
				Printf(TEXTCOLOR_RED "string expected for '%s'", key);
				pstr = "";
				arc.mErrors++;
			}
		}
	}
	return arc;

}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr, char **def)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || def == nullptr || strcmp(pstr, *def))
		{
			arc.WriteKey(key);
			if (pstr == nullptr)
			{
				arc.w->Null();
			}
			else
			{
				arc.w->String(pstr);
			}
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		if (val != nullptr)
		{
			assert(val->IsNull() || val->IsString());
			if (val->IsNull())
			{
				pstr = nullptr;
			}
			else if (val->IsString())
			{
				pstr = copystring(UnicodeToString(val->GetString()));
			}
			else
			{
				Printf(TEXTCOLOR_RED "string expected for '%s'", key);
				pstr = nullptr;
				arc.mErrors++;
			}
		}
	}
	return arc;
}

template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def)
{
	if (arc.isWriting())
	{
		const char *n = font->GetName();
		return arc.StringPtr(key, n);
	}
	else
	{
		const char *n;
		arc.StringPtr(key, n);
		font = V_GetFont(n);
		if (font == nullptr)
		{
			Printf(TEXTCOLOR_ORANGE "Could not load font %s\n", n);
			font = SmallFont;
		}
		return arc;
	}

}

// Handler to retrieve a numeric value of any kind.

FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &value, NumericValue *defval)
{
	if (arc.isWriting())
	{
		if (!arc.w->inObject() || defval == nullptr || value != *defval)
		{
			arc.WriteKey(key);
			switch (value.type)
			{
			case NumericValue::NM_signed:
				arc.w->Int64(value.signedval);
				break;
			case NumericValue::NM_unsigned:
				arc.w->Uint64(value.unsignedval);
				break;
			case NumericValue::NM_float:
				arc.w->Double(value.floatval);
				break;
			default:
				arc.w->Null();
				break;
			}
		}
	}
	else
	{
		auto val = arc.r->FindKey(key);
		value.signedval = 0;
		value.type = NumericValue::NM_invalid;
		if (val != nullptr)
		{
			if (val->IsUint64())
			{
				value.unsignedval = val->GetUint64();
				value.type = NumericValue::NM_unsigned;
			}
			else if (val->IsInt64())
			{
				value.signedval = val->GetInt64();
				value.type = NumericValue::NM_signed;
			}
			else if (val->IsDouble())
			{
				value.floatval = val->GetDouble();
				value.type = NumericValue::NM_float;
			}
		}
	}
	return arc;
}