<commit_before>// Loosely based on https://repo.anl-external.org/repos/BlueTBB/tbb30_104oss/src/rml/perfor/tbb_simple.cpp

// #include "stdafx.h"

// int _tmain(int argc, _TCHAR* argv[])
// {
//	return 0;
// }

#include "tbb/task_group.h"

#include <iostream>
#include <stdio.h> //// MOVE TO iostreams TODO
#include <stdlib.h>

#include <tbb/mutex.h>
tbb::mutex my_mutex;

#if _WIN32||_WIN64
#include <Windows.h> /* Sleep */
#else
//// #error Only tested on Windows! TODO
#include <unistd.h>  /* usleep */
#endif

using namespace tbb;

#if 0 // totally inappropriate and broken use of subclassing of tbb::task, just for giggles
// see https://www.threadingbuildingblocks.org/docs/help/reference/task_scheduler/task_cls.htm
#include "tbb/task.h"

class TrivialTask : public tbb::task {
public:

    TrivialTask() {	}

    task* parent() const { // the "parent" member-function is confusingly referred to as "the successor attribute"
        return NULL;
    }

    task* execute() override {
        puts("Hello from my task!");
        return NULL;
    }

    ~TrivialTask() {
        puts("Destroying my task");
    }
};

// allocate_continuation
// #include <stdio.h>
// #include <stdlib.h>
int main(int argc, char *argv[]) {
    // use TBB's custom allocator, along the lines shown in
    // https://www.threadingbuildingblocks.org/docs/help/tbb_userguide/Simple_Example_Fibonacci_Numbers.htm#tutorial_Simple_Example_Fibonacci_Numbers

    // see also https://www.threadingbuildingblocks.org/docs/help/reference/task_scheduler/task_allocation.htm

    {
        // looking at task_allocation.htm this should use:
        // "cancellation group is the current innermost cancellation group. "
        ////// WHICH WE DON'T HAVE!?!?! shouldn't this cause an assert failure?
        task& t = *new(task::allocate_root()) TrivialTask; // following http://fulla.fnal.gov/intel/tbb/html/a00249.html

        auto count1 = t.ref_count();

        t.execute(); // No concurrency! Hence 'totally inappropriate'
                     // t.decrement_ref_count(); causes underflow!
                     //if ( NULL != t.parent ) {
                     //}

                     // "Because there is no placement delete expression[...]" https://en.wikipedia.org/w/index.php?title=Placement_syntax&oldid=674756226#Custom_allocators

                     // not using standard 'new' so can't use standard 'delete'
                     //delete (task::allocate_root()) &t; // kaboom! VS2010 picks up a double-free problem. Looks like we're missing something important....????

        auto count2 = t.ref_count();

        t.decrement_ref_count(); // same as   t.set_ref_count(0);

    }

    return EXIT_SUCCESS;
}
#endif

#if 0
int main(int argc, char *argv[]) {
    puts("Starting...");

    task_group g;

    g.run([&] {Sleep(2000); puts("Task 1 is away"); });
    g.run([&] {Sleep(2000); puts("Task 2 is away"); });
    g.run([&] {Sleep(2000); puts("Task 3 is away"); });

    g.run_and_wait([&] {Sleep(2000); puts("A message from the main thread"); });

    //////g.wait();
    // getchar();
    return EXIT_SUCCESS;
}
#endif

// demonstrate the const issue with run_and_wait
#if 0
int main(int argc, char *argv[]) {
    puts("Starting main thread...");

    class MyCallable {
    public:
      MyCallable() {} // clang++ doesn't like 'const' instance of empty class without explicitly defined constructor
// Normally, using an empty class we avoid having to define our own default constructor
// (even if MSVC is permissive here) see http://stackoverflow.com/a/8092791/2307853
//    int dummy;

        void operator()() const {
            puts("Task is running");
        }
    };

    const MyCallable f;

    {
        task_group g;

        // g.run( f ); g.wait();
        g.run_and_wait( f );

//      g.run_and_wait( [&]() { f(); } );
//      g.run_and_wait( [&]() { } );
        // C++ lambdas seem to survive the const shenanigans, but MyCallable doesn't: build error
    }

    puts("Press Enter to exit");
    getchar();
    return EXIT_SUCCESS;
}
#endif

#if 1 // fun with fake future

// #include "tbb/atomic.h"

// futures in TBB

/// NOTE this example is bad, and only ever worked because VS was seemingly failing to enforce 'const' restrictions
/// I've commented out the mutation of 'result' and added' const', but this doesn't leave it doing any real work...

int main(int argc, char *argv[]) {
    puts("Starting...");

    class MyFuture {
    public:
        int result;

        //		atomic<int> executionHasBegun;
        //		atomic<bool> executionIsComplete;

        MyFuture() :
            result(-1)
            //			,executionHasBegun(),
            //		    ,executionIsComplete() /*Uninitialised at this point. This DOES NOT assign false.*/
        {
            //			this->executionHasBegun = false;
            //			this->executionIsComplete = false; // needlessly atomic assignment (can't opt-out of atomicity here, can we?)
        }

        void operator()() {
            // atomics consistency rules are specified at https://software.intel.com/en-us/node/506092

            // this->executionHasBegun = true; //  unsafe. 'release' semantics ==> "at least this late but possibly later"
            // this->executionHasBegun = 1;    //  unsafe. 'release' semantics ==> "at least this late but possibly later"

            // (using atomic<bool> rather than atomic<bool>) this doesn't work! only <release> is permitted!
            //this->executionHasBegun.store<acquire>(true); // force ues of acquire semantics: "at least this early, but possibly earlier"

            //			this->executionHasBegun.fetch_and_increment<acquire>(); // safe. fully sequentially consistent, huzzah

            puts("Task is running");

            result = 3; // XXX

            // this time the default of "at least this late" semantics is appropriate, so we leave the default semantics
            // this->executionIsComplete = true; // guaranteed to happen only after outInt has been assigned, and to propagate as expected
            return;
        }

        int getResult(task_group& tg) const {
            //			if ( !this->executionHasBegun ) {
            //				tg.run(*this); // possibly a really bad idea..... futures are meant to be started manually!
            //			}

            //			if ( !this->executionIsComplete ) {
            tg.wait();
            //			}
            return this->result;
        }

    };

    int myOutInt = -1;
    const MyFuture f;

    {
        task_group g;

//      g.run(f); g.wait();
        //g.run_and_wait(f); // this doesn't work! VS2010 compiler complains of type trouble. seems to cast from const& to &
                           // what's going on here? curiously the arg is *not* the same type as that of the "run" member-function !!!
        g.run_and_wait( [&]() mutable {f();} ); // yes, this craziness *is* necessary!

        printf( "%d\n", f.getResult(g) );

        //g.wait(); // Do nothing. Idempotency of waiting.
        //g.wait();
        //g.wait();
    }

    //g.run( [&]{Sleep(2000);puts("Task 1 is away");} );
    //g.run( [&]{Sleep(2000);puts("Task 2 is away");} );
    //g.run( [&]{Sleep(2000);puts("Task 3 is away");} );

    //g.run_and_wait( [&]{Sleep(2000); puts("A message from the main thread");} );

    //////g.wait();
    // getchar();
    return EXIT_SUCCESS;
}
#endif

#if 0 // the fib example from the web
int fib(int n) {
    int ret;
    if (n < 2) {
        ret = n;
    }
    else {
        int x, y;
        task_group g;

        x = y = 9;

        // g.run( [&]{my_mutex.lock();/* x=fib(n-1); */ puts("Now:\n"); getchar(); my_mutex.unlock();} ); // spawn a task

        g.run([&] {
            tbb::mutex::scoped_lock lock(my_mutex);
            /* x=fib(n-1); */ puts("Now:\n"); getchar();
        }); // spawn a task

            // g.run( [&]{my_mutex.lock();/* y=fib(n-2); */ puts("Now:\n"); getchar(); my_mutex.unlock();} ); // spawn another task
        g.run([&] {
            tbb::mutex::scoped_lock lock(my_mutex);/* y=fib(n-2); */ puts("Now:\n"); getchar();
        }); // spawn another task

        g.wait();                // wait for both tasks to complete

        ret = x + y;
    }

    return ret;
} // - See more at: https://www.threadingbuildingblocks.org/tutorial-intel-tbb-task-based-programming#sthash.EzgRXzaB.dpuf

int main(int argc, char *argv[]) {
    std::cout << fib(33);

    // getchar();
    return EXIT_SUCCESS;
}

#endif
<commit_msg>Resolve const issues: move mutable state out of functor<commit_after>// Loosely based on https://repo.anl-external.org/repos/BlueTBB/tbb30_104oss/src/rml/perfor/tbb_simple.cpp

// #include "stdafx.h"

// int _tmain(int argc, _TCHAR* argv[])
// {
//	return 0;
// }

#include "tbb/task_group.h"

#include <iostream>
#include <stdio.h> //// MOVE TO iostreams TODO
#include <stdlib.h>

#include <tbb/mutex.h>
tbb::mutex my_mutex;

#if _WIN32||_WIN64
#include <Windows.h> /* Sleep */
#else
//// #error Only tested on Windows! TODO
#include <unistd.h>  /* usleep */
#endif

using namespace tbb;

#if 0 // totally inappropriate and broken use of subclassing of tbb::task, just for giggles
// see https://www.threadingbuildingblocks.org/docs/help/reference/task_scheduler/task_cls.htm
#include "tbb/task.h"

class TrivialTask : public tbb::task {
public:

    TrivialTask() {	}

    task* parent() const { // the "parent" member-function is confusingly referred to as "the successor attribute"
        return NULL;
    }

    task* execute() override {
        puts("Hello from my task!");
        return NULL;
    }

    ~TrivialTask() {
        puts("Destroying my task");
    }
};

// allocate_continuation
// #include <stdio.h>
// #include <stdlib.h>
int main(int argc, char *argv[]) {
    // use TBB's custom allocator, along the lines shown in
    // https://www.threadingbuildingblocks.org/docs/help/tbb_userguide/Simple_Example_Fibonacci_Numbers.htm#tutorial_Simple_Example_Fibonacci_Numbers

    // see also https://www.threadingbuildingblocks.org/docs/help/reference/task_scheduler/task_allocation.htm

    {
        // looking at task_allocation.htm this should use:
        // "cancellation group is the current innermost cancellation group. "
        ////// WHICH WE DON'T HAVE!?!?! shouldn't this cause an assert failure?
        task& t = *new(task::allocate_root()) TrivialTask; // following http://fulla.fnal.gov/intel/tbb/html/a00249.html

        auto count1 = t.ref_count();

        t.execute(); // No concurrency! Hence 'totally inappropriate'
                     // t.decrement_ref_count(); causes underflow!
                     //if ( NULL != t.parent ) {
                     //}

                     // "Because there is no placement delete expression[...]" https://en.wikipedia.org/w/index.php?title=Placement_syntax&oldid=674756226#Custom_allocators

                     // not using standard 'new' so can't use standard 'delete'
                     //delete (task::allocate_root()) &t; // kaboom! VS2010 picks up a double-free problem. Looks like we're missing something important....????

        auto count2 = t.ref_count();

        t.decrement_ref_count(); // same as   t.set_ref_count(0);

    }

    return EXIT_SUCCESS;
}
#endif

#if 0
int main(int argc, char *argv[]) {
    puts("Starting...");

    task_group g;

    g.run([&] {Sleep(2000); puts("Task 1 is away"); });
    g.run([&] {Sleep(2000); puts("Task 2 is away"); });
    g.run([&] {Sleep(2000); puts("Task 3 is away"); });

    g.run_and_wait([&] {Sleep(2000); puts("A message from the main thread"); });

    //////g.wait();
    // getchar();
    return EXIT_SUCCESS;
}
#endif

// demonstrate the const issue with run_and_wait
#if 0
int main(int argc, char *argv[]) {
    puts("Starting main thread...");

    class MyCallable {
    public:
      MyCallable() {} // clang++ doesn't like 'const' instance of empty class without explicitly defined constructor
// Normally, using an empty class we avoid having to define our own default constructor
// (even if MSVC is permissive here) see http://stackoverflow.com/a/8092791/2307853
//    int dummy;

        void operator()() const {
            puts("Task is running");
        }
    };

    const MyCallable f;

    {
        task_group g;

        // g.run( f ); g.wait();
        g.run_and_wait( f );

//      g.run_and_wait( [&]() { f(); } );
//      g.run_and_wait( [&]() { } );
        // C++ lambdas seem to survive the const shenanigans, but MyCallable doesn't: build error
    }

    puts("Press Enter to exit");
    getchar();
    return EXIT_SUCCESS;
}
#endif

#if 1 // fun with fake future

// #include "tbb/atomic.h"

// futures in TBB

/// NOTE this example is bad, and only ever worked because VS was seemingly failing to enforce 'const' restrictions
/// I've commented out the mutation of 'result' and added' const', but this doesn't leave it doing any real work...

int main(int argc, char *argv[]) {
    puts("Starting...");

    class MyFuture {
    public:

        struct MutableState {
          MutableState(int r) : result(r) { }
          int result;
        };

        task_group * tgPtr;
        MutableState * mutableStatePtr;

        //		atomic<int> executionHasBegun;
        //		atomic<bool> executionIsComplete;

        MyFuture(task_group *t, MutableState * m) :
            tgPtr(t),
            mutableStatePtr(m)
            //			,executionHasBegun(),
            //		    ,executionIsComplete() /*Uninitialised at this point. This DOES NOT assign false.*/
        {
            //			this->executionHasBegun = false;
            //			this->executionIsComplete = false; // needlessly atomic assignment (can't opt-out of atomicity here, can we?)
        }

        void operator()() const {
            // atomics consistency rules are specified at https://software.intel.com/en-us/node/506092

            // this->executionHasBegun = true; //  unsafe. 'release' semantics ==> "at least this late but possibly later"
            // this->executionHasBegun = 1;    //  unsafe. 'release' semantics ==> "at least this late but possibly later"

            // (using atomic<bool> rather than atomic<bool>) this doesn't work! only <release> is permitted!
            //this->executionHasBegun.store<acquire>(true); // force ues of acquire semantics: "at least this early, but possibly earlier"

            //			this->executionHasBegun.fetch_and_increment<acquire>(); // safe. fully sequentially consistent, huzzah

            puts("Task is running");

            mutableStatePtr->result = 3;

            // this time the default of "at least this late" semantics is appropriate, so we leave the default semantics
            // this->executionIsComplete = true; // guaranteed to happen only after outInt has been assigned, and to propagate as expected
            return;
        }

        int getResult() const {
            //			if ( !this->executionHasBegun ) {
            //				tg.run(*this); // possibly a really bad idea..... futures are meant to be started manually!
            //			}

            //			if ( !this->executionIsComplete ) {
            tgPtr->wait();
            //			}
            return mutableStatePtr->result;
        }

    };

    int modifyMe = 0;

    MyFuture::MutableState ms(0);

    task_group g;

    const MyFuture f(&g, &ms);

//      g.run(f); g.wait();
    //g.run_and_wait(f); // this doesn't work! VS2010 compiler complains of type trouble. seems to cast from const& to &
                           // what's going on here? curiously the arg is *not* the same type as that of the "run" member-function !!!
    g.run_and_wait( [&](){f();} ); // yes, this craziness *is* necessary!

    printf( "%d\n", f.getResult() );

    //g.wait(); // Do nothing. Idempotency of waiting.
    //g.wait();

    //g.run( [&]{Sleep(2000);puts("Task 1 is away");} );
    //g.run( [&]{Sleep(2000);puts("Task 2 is away");} );
    //g.run( [&]{Sleep(2000);puts("Task 3 is away");} );

    //g.run_and_wait( [&]{Sleep(2000); puts("A message from the main thread");} );

    //////g.wait();
    // getchar();
    return EXIT_SUCCESS;
}
#endif

#if 0 // the fib example from the web
int fib(int n) {
    int ret;
    if (n < 2) {
        ret = n;
    }
    else {
        int x, y;
        task_group g;

        x = y = 9;

        // g.run( [&]{my_mutex.lock();/* x=fib(n-1); */ puts("Now:\n"); getchar(); my_mutex.unlock();} ); // spawn a task

        g.run([&] {
            tbb::mutex::scoped_lock lock(my_mutex);
            /* x=fib(n-1); */ puts("Now:\n"); getchar();
        }); // spawn a task

            // g.run( [&]{my_mutex.lock();/* y=fib(n-2); */ puts("Now:\n"); getchar(); my_mutex.unlock();} ); // spawn another task
        g.run([&] {
            tbb::mutex::scoped_lock lock(my_mutex);/* y=fib(n-2); */ puts("Now:\n"); getchar();
        }); // spawn another task

        g.wait();                // wait for both tasks to complete

        ret = x + y;
    }

    return ret;
} // - See more at: https://www.threadingbuildingblocks.org/tutorial-intel-tbb-task-based-programming#sthash.EzgRXzaB.dpuf

int main(int argc, char *argv[]) {
    std::cout << fib(33);

    // getchar();
    return EXIT_SUCCESS;
}

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/tracing/core/packet_stream_validator.h"

#include <inttypes.h>
#include <stddef.h>

#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/proto_utils.h"

#include "protos/perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {

namespace {

using protozero::proto_utils::ProtoWireType;

const uint32_t kReservedFieldIds[] = {
    protos::pbzero::TracePacket::kTrustedUidFieldNumber,
    protos::pbzero::TracePacket::kTrustedPacketSequenceIdFieldNumber,
    protos::pbzero::TracePacket::kTraceConfigFieldNumber,
    protos::pbzero::TracePacket::kTraceStatsFieldNumber,
    protos::pbzero::TracePacket::kCompressedPacketsFieldNumber,
    protos::pbzero::TracePacket::kSynchronizationMarkerFieldNumber,
};

// This translation unit is quite subtle and perf-sensitive. Remember to check
// BM_PacketStreamValidator in perfetto_benchmarks when making changes.

// Checks that a packet, spread over several slices, is well-formed and doesn't
// contain reserved top-level fields.
// The checking logic is based on a state-machine that skips the fields' payload
// and operates as follows:
//              +-------------------------------+ <-------------------------+
// +----------> | Read field preamble (varint)  | <----------------------+  |
// |            +-------------------------------+                        |  |
// |              |              |            |                          |  |
// |       <Varint>        <Fixed 32/64>     <Length-delimited field>    |  |
// |          V                  |                      V                |  |
// |  +------------------+       |               +--------------+        |  |
// |  | Read field value |       |               | Read length  |        |  |
// |  | (another varint) |       |               |   (varint)   |        |  |
// |  +------------------+       |               +--------------+        |  |
// |           |                 V                      V                |  |
// +-----------+        +----------------+     +-----------------+       |  |
//                      | Skip 4/8 Bytes |     | Skip $len Bytes |-------+  |
//                      +----------------+     +-----------------+          |
//                               |                                          |
//                               +------------------------------------------+
class ProtoFieldParserFSM {
 public:
  // This method effectively continuously parses varints (either for the field
  // preamble or the payload or the submessage length) and tells the caller
  // (the Validate() method) how many bytes to skip until the next field.
  size_t Push(uint8_t octet) {
    varint_ |= static_cast<uint64_t>(octet & 0x7F) << varint_shift_;
    if (octet & 0x80) {
      varint_shift_ += 7;
      if (varint_shift_ >= 64)
        state_ = kInvalidVarInt;
      return 0;
    }
    uint64_t varint = varint_;
    varint_ = 0;
    varint_shift_ = 0;

    switch (state_) {
      case kFieldPreamble: {
        uint64_t field_type = varint & 7;  // 7 = 0..0111
        auto field_id = static_cast<uint32_t>(varint >> 3);
        // Check if the field id is reserved, go into an error state if it is.
        for (size_t i = 0; i < base::ArraySize(kReservedFieldIds); ++i) {
          if (field_id == kReservedFieldIds[i]) {
            state_ = kWroteReservedField;
            return 0;
          }
        }
        // The field type is legit, now check it's well formed and within
        // boundaries.
        if (field_type == static_cast<uint64_t>(ProtoWireType::kVarInt)) {
          state_ = kVarIntValue;
        } else if (field_type ==
                   static_cast<uint64_t>(ProtoWireType::kFixed32)) {
          return 4;
        } else if (field_type ==
                   static_cast<uint64_t>(ProtoWireType::kFixed64)) {
          return 8;
        } else if (field_type ==
                   static_cast<uint64_t>(ProtoWireType::kLengthDelimited)) {
          state_ = kLenDelimitedLen;
        } else {
          state_ = kUnknownFieldType;
        }
        return 0;
      }

      case kVarIntValue: {
        // Consume the int field payload and go back to the next field.
        state_ = kFieldPreamble;
        return 0;
      }

      case kLenDelimitedLen: {
        if (varint > protozero::proto_utils::kMaxMessageLength) {
          state_ = kMessageTooBig;
          return 0;
        }
        state_ = kFieldPreamble;
        return static_cast<size_t>(varint);
      }

      case kWroteReservedField:
      case kUnknownFieldType:
      case kMessageTooBig:
      case kInvalidVarInt:
        // Persistent error states.
        return 0;

    }          // switch(state_)
    return 0;  // To keep GCC happy.
  }

  // Queried at the end of the all payload. A message is well-formed only
  // if the FSM is back to the state where it should parse the next field and
  // hasn't started parsing any preamble.
  bool valid() const { return state_ == kFieldPreamble && varint_shift_ == 0; }
  int state() const { return static_cast<int>(state_); }

 private:
  enum State {
    kFieldPreamble = 0,  // Parsing the varint for the field preamble.
    kVarIntValue,        // Parsing the varint value for the field payload.
    kLenDelimitedLen,    // Parsing the length of the length-delimited field.

    // Error states:
    kWroteReservedField,  // Tried to set a reserved field id.
    kUnknownFieldType,    // Encountered an invalid field type.
    kMessageTooBig,       // Size of the length delimited message was too big.
    kInvalidVarInt,       // VarInt larger than 64 bits.
  };

  State state_ = kFieldPreamble;
  uint64_t varint_ = 0;
  uint32_t varint_shift_ = 0;
};

}  // namespace

// static
bool PacketStreamValidator::Validate(const Slices& slices) {
  ProtoFieldParserFSM parser;
  size_t skip_bytes = 0;
  for (const Slice& slice : slices) {
    for (size_t i = 0; i < slice.size;) {
      const size_t skip_bytes_cur_slice = std::min(skip_bytes, slice.size - i);
      if (skip_bytes_cur_slice > 0) {
        i += skip_bytes_cur_slice;
        skip_bytes -= skip_bytes_cur_slice;
      } else {
        uint8_t octet = *(reinterpret_cast<const uint8_t*>(slice.start) + i);
        skip_bytes = parser.Push(octet);
        i++;
      }
    }
  }
  if (skip_bytes == 0 && parser.valid())
    return true;

  PERFETTO_DLOG("Packet validation error (state %d, skip = %zu)",
                parser.state(), skip_bytes);
  return false;
}

}  // namespace perfetto
<commit_msg>Do not attempt out of range shift. am: e34fab41d6 am: 46ab3fa1ce<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/tracing/core/packet_stream_validator.h"

#include <inttypes.h>
#include <stddef.h>

#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/proto_utils.h"

#include "protos/perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {

namespace {

using protozero::proto_utils::ProtoWireType;

const uint32_t kReservedFieldIds[] = {
    protos::pbzero::TracePacket::kTrustedUidFieldNumber,
    protos::pbzero::TracePacket::kTrustedPacketSequenceIdFieldNumber,
    protos::pbzero::TracePacket::kTraceConfigFieldNumber,
    protos::pbzero::TracePacket::kTraceStatsFieldNumber,
    protos::pbzero::TracePacket::kCompressedPacketsFieldNumber,
    protos::pbzero::TracePacket::kSynchronizationMarkerFieldNumber,
};

// This translation unit is quite subtle and perf-sensitive. Remember to check
// BM_PacketStreamValidator in perfetto_benchmarks when making changes.

// Checks that a packet, spread over several slices, is well-formed and doesn't
// contain reserved top-level fields.
// The checking logic is based on a state-machine that skips the fields' payload
// and operates as follows:
//              +-------------------------------+ <-------------------------+
// +----------> | Read field preamble (varint)  | <----------------------+  |
// |            +-------------------------------+                        |  |
// |              |              |            |                          |  |
// |       <Varint>        <Fixed 32/64>     <Length-delimited field>    |  |
// |          V                  |                      V                |  |
// |  +------------------+       |               +--------------+        |  |
// |  | Read field value |       |               | Read length  |        |  |
// |  | (another varint) |       |               |   (varint)   |        |  |
// |  +------------------+       |               +--------------+        |  |
// |           |                 V                      V                |  |
// +-----------+        +----------------+     +-----------------+       |  |
//                      | Skip 4/8 Bytes |     | Skip $len Bytes |-------+  |
//                      +----------------+     +-----------------+          |
//                               |                                          |
//                               +------------------------------------------+
class ProtoFieldParserFSM {
 public:
  // This method effectively continuously parses varints (either for the field
  // preamble or the payload or the submessage length) and tells the caller
  // (the Validate() method) how many bytes to skip until the next field.
  size_t Push(uint8_t octet) {
    varint_ |= static_cast<uint64_t>(octet & 0x7F) << varint_shift_;
    if (octet & 0x80) {
      varint_shift_ += 7;
      if (varint_shift_ >= 64) {
        // Do not invoke UB on next call.
        varint_shift_ = 0;
        state_ = kInvalidVarInt;
      }
      return 0;
    }
    uint64_t varint = varint_;
    varint_ = 0;
    varint_shift_ = 0;

    switch (state_) {
      case kFieldPreamble: {
        uint64_t field_type = varint & 7;  // 7 = 0..0111
        auto field_id = static_cast<uint32_t>(varint >> 3);
        // Check if the field id is reserved, go into an error state if it is.
        for (size_t i = 0; i < base::ArraySize(kReservedFieldIds); ++i) {
          if (field_id == kReservedFieldIds[i]) {
            state_ = kWroteReservedField;
            return 0;
          }
        }
        // The field type is legit, now check it's well formed and within
        // boundaries.
        if (field_type == static_cast<uint64_t>(ProtoWireType::kVarInt)) {
          state_ = kVarIntValue;
        } else if (field_type ==
                   static_cast<uint64_t>(ProtoWireType::kFixed32)) {
          return 4;
        } else if (field_type ==
                   static_cast<uint64_t>(ProtoWireType::kFixed64)) {
          return 8;
        } else if (field_type ==
                   static_cast<uint64_t>(ProtoWireType::kLengthDelimited)) {
          state_ = kLenDelimitedLen;
        } else {
          state_ = kUnknownFieldType;
        }
        return 0;
      }

      case kVarIntValue: {
        // Consume the int field payload and go back to the next field.
        state_ = kFieldPreamble;
        return 0;
      }

      case kLenDelimitedLen: {
        if (varint > protozero::proto_utils::kMaxMessageLength) {
          state_ = kMessageTooBig;
          return 0;
        }
        state_ = kFieldPreamble;
        return static_cast<size_t>(varint);
      }

      case kWroteReservedField:
      case kUnknownFieldType:
      case kMessageTooBig:
      case kInvalidVarInt:
        // Persistent error states.
        return 0;

    }          // switch(state_)
    return 0;  // To keep GCC happy.
  }

  // Queried at the end of the all payload. A message is well-formed only
  // if the FSM is back to the state where it should parse the next field and
  // hasn't started parsing any preamble.
  bool valid() const { return state_ == kFieldPreamble && varint_shift_ == 0; }
  int state() const { return static_cast<int>(state_); }

 private:
  enum State {
    kFieldPreamble = 0,  // Parsing the varint for the field preamble.
    kVarIntValue,        // Parsing the varint value for the field payload.
    kLenDelimitedLen,    // Parsing the length of the length-delimited field.

    // Error states:
    kWroteReservedField,  // Tried to set a reserved field id.
    kUnknownFieldType,    // Encountered an invalid field type.
    kMessageTooBig,       // Size of the length delimited message was too big.
    kInvalidVarInt,       // VarInt larger than 64 bits.
  };

  State state_ = kFieldPreamble;
  uint64_t varint_ = 0;
  uint32_t varint_shift_ = 0;
};

}  // namespace

// static
bool PacketStreamValidator::Validate(const Slices& slices) {
  ProtoFieldParserFSM parser;
  size_t skip_bytes = 0;
  for (const Slice& slice : slices) {
    for (size_t i = 0; i < slice.size;) {
      const size_t skip_bytes_cur_slice = std::min(skip_bytes, slice.size - i);
      if (skip_bytes_cur_slice > 0) {
        i += skip_bytes_cur_slice;
        skip_bytes -= skip_bytes_cur_slice;
      } else {
        uint8_t octet = *(reinterpret_cast<const uint8_t*>(slice.start) + i);
        skip_bytes = parser.Push(octet);
        i++;
      }
    }
  }
  if (skip_bytes == 0 && parser.valid())
    return true;

  PERFETTO_DLOG("Packet validation error (state %d, skip = %zu)",
                parser.state(), skip_bytes);
  return false;
}

}  // namespace perfetto
<|endoftext|>


<commit_before>// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include <array>
#include <atomic>
#include <cmath>
#include <functional>
#include <iostream>
#include <iterator>
#include <mutex>
#include <thread>

#include <franka/duration.h>
#include <franka/exception.h>
#include <franka/model.h>
#include <franka/robot.h>

namespace {
template <class T, size_t N>
std::ostream& operator<<(std::ostream& ostream, const std::array<T, N>& array) {
  ostream << "[";
  std::copy(array.cbegin(), array.cend() - 1, std::ostream_iterator<T>(ostream, ","));
  std::copy(array.cend() - 1, array.cend(), std::ostream_iterator<T>(ostream));
  ostream << "]";
  return ostream;
}
}  // anonymous namespace

/**
 * @example joint_impedance_control.cpp
 * An example showing a joint impedance type control that executes a Cartesian motion in the shape
 * of a circle. The example illustrates how to use the internal inverse kinematics to map a
 * Cartesian trajectory to joint space. The joint space target is tracked by an impedance control
 * that additionally compensates coriolis terms using the libfranka model library. This example also
 * serves to compare commanded vs. measured torques. The results are printed from a separate thread
 * to avoid blocking print functions in the real-time loop.
 */

std::array<double, 7> saturateTorqueRate(
    const double delta_tau_max,
    const std::array<double, 7>& tau_d_calculated,
    const std::array<double, 7>& tau_J_d,  // NOLINT (readability-identifier-naming)
    const std::array<double, 7>& gravity);

int main(int argc, char** argv) {
  // Check whether the required arguments were passed.
  if (argc != 5) {
    std::cerr << "Usage: ./" << argv[0] << " <robot-hostname>"
              << " <radius in [m]>"
              << " <vel_max in [m/s]>"
              << " <print_rate in [Hz]>" << std::endl;
    return -1;
  }

  // Set and initialize trajectory parameters.
  const double radius = std::stod(argv[2]);
  const double vel_max = std::stod(argv[3]);
  const double acceleration_time = 2.0;
  double vel_current = 0.0;
  double angle = 0.0;

  double time = 0.0;
  const double run_time = 20.0;

  // Set print rate for comparing commanded vs. measured torques.
  double print_rate = std::stod(argv[4]);
  if (print_rate < 0.0) {
    std::cerr << "print_rate too small, must be >= 0.0" << std::endl;
    return -1;
  }

  // Initialize data fields for the print thread.
  struct {
    std::mutex mutex;
    bool has_data;
    std::array<double, 7> tau_d_last;
    franka::RobotState robot_state;
    std::array<double, 7> gravity;
  } print_data{};
  std::atomic_bool running{true};

  // Start print thread.
  std::thread print_thread([print_rate, &print_data, &running]() {
    while (running) {
      // Sleep to achieve the desired print rate.
      std::this_thread::sleep_for(
          std::chrono::milliseconds(static_cast<int>((1.0 / print_rate * 1000.0))));

      // Try to lock data to avoid read write collisions.
      if (print_data.mutex.try_lock() && print_data.has_data) {
        std::array<double, 7> tau_error{};
        double error_rms(0.0);
        std::array<double, 7> tau_d_actual{};
        for (size_t i = 0; i < 7; ++i) {
          tau_d_actual[i] = print_data.tau_d_last[i] + print_data.gravity[i];
          tau_error[i] = tau_d_actual[i] - print_data.robot_state.tau_J[i];
          error_rms += std::sqrt(std::pow(tau_error[i], 2.0)) / tau_error.size();
        }
        // Print data to console
        std::cout << "tau_error [Nm]: " << tau_error << std::endl
                  << "tau_commanded [Nm]: " << tau_d_actual << std::endl
                  << "tau_measured [Nm]: " << print_data.robot_state.tau_J << std::endl
                  << "root mean square of tau_error [Nm]: " << error_rms << std::endl
                  << "-----------------------" << std::endl;
        print_data.has_data = false;
        print_data.mutex.unlock();
      }
    }
  });

  try {
    // Connect to robot.
    franka::Robot robot(argv[1]);

    // Set collision behavior.
    robot.setCollisionBehavior(
        {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}},
        {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}});

    // Load the kinematics and dynamics model.
    franka::Model model = robot.loadModel();

    // Read the initial pose to start the motion from there.
    std::array<double, 16> initial_pose = robot.readOnce().O_T_EE;

    // Define callback function to send Cartesian pose goals to get inverse kinematics solved.
    std::function<franka::CartesianPose(const franka::RobotState&, franka::Duration)>
        cartesian_pose_callback = [=, &time, &vel_current, &running, &angle](
            const franka::RobotState& /*state*/, franka::Duration period) -> franka::CartesianPose {
      // Update time.
      time += period.toSec();

      // Compute Cartesian velocity.
      if (vel_current < vel_max && time < run_time) {
        vel_current += period.toSec() * std::fabs(vel_max / acceleration_time);
      }
      if (vel_current > 0.0 && time > run_time) {
        vel_current -= period.toSec() * std::fabs(vel_max / acceleration_time);
      }
      vel_current = std::fmax(vel_current, 0.0);
      vel_current = std::fmin(vel_current, vel_max);

      // Compute new angle for our circular trajectory.
      angle += period.toSec() * vel_current / std::fabs(radius);
      if (angle > 2 * M_PI) {
        angle -= 2 * M_PI;
      }

      // Compute relative y and z positions of desired pose.
      double delta_y = radius * (1 - std::cos(angle));
      double delta_z = radius * std::sin(angle);
      franka::CartesianPose pose_desired = initial_pose;
      pose_desired.O_T_EE[13] += delta_y;
      pose_desired.O_T_EE[14] += delta_z;

      // Send desired pose.
      if (time >= run_time + acceleration_time) {
        running = false;
        return franka::MotionFinished(pose_desired);
      }

      return pose_desired;
    };

    // Set gains for the joint impedance control.
    // Stiffness
    const std::array<double, 7> k_gains = {{600.0, 600.0, 600.0, 600.0, 250.0, 150.0, 50.0}};
    // Damping
    const std::array<double, 7> d_gains = {{50.0, 50.0, 50.0, 50.0, 30.0, 25.0, 15.0}};

    // Maximum torque difference.
    const double delta_tau_max = 1.0;

    // Define callback for the joint torque control loop.
    std::function<franka::Torques(const franka::RobotState&, franka::Duration)>
        impedance_control_callback = [&print_data, &model, k_gains, d_gains, delta_tau_max](
            const franka::RobotState& state, franka::Duration /*period*/) -> franka::Torques {
      // Read current coriolis terms from model.
      std::array<double, 7> coriolis = model.coriolis(
          state, {{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}}, 0.0, {{0.0, 0.0, 0.0}});

      // Compute torque command from joint impedance control law.
      // Note: The answer to our Cartesian pose inverse kinematics is always in state.q_d with one
      // time step delay.
      std::array<double, 7> tau_d_calculated;
      for (size_t i = 0; i < 7; i++) {
        tau_d_calculated[i] =
            k_gains[i] * (state.q_d[i] - state.q[i]) - d_gains[i] * state.dq[i] + coriolis[i];
      }

      std::array<double, 7> tau_d_saturated =
          saturateTorqueRate(delta_tau_max, tau_d_calculated, state.tau_J_d,
                             model.gravity(state, 0.0, {{0.0, 0.0, 0.0}}));

      // Update data to print.
      if (print_data.mutex.try_lock()) {
        print_data.has_data = true;
        print_data.robot_state = state;
        print_data.tau_d_last = tau_d_saturated;
        print_data.gravity = model.gravity(state, 0.0, {{0.0, 0.0, 0.0}});
        print_data.mutex.unlock();
      }

      // Send torque command.
      return tau_d_saturated;
    };

    // Start real-time control loop.
    robot.control(impedance_control_callback, cartesian_pose_callback);

  } catch (const franka::Exception& ex) {
    running = false;
    std::cerr << ex.what() << std::endl;
  }

  if (print_thread.joinable()) {
    print_thread.join();
  }
  return 0;
}

std::array<double, 7> saturateTorqueRate(
    const double delta_tau_max,
    const std::array<double, 7>& tau_d_calculated,
    const std::array<double, 7>& tau_J_d,  // NOLINT (readability-identifier-naming)
    const std::array<double, 7>& gravity) {
  std::array<double, 7> tau_d_saturated{};
  for (size_t i = 0; i < 7; i++) {
    double difference = tau_d_calculated[i] - (tau_J_d[i] - gravity[i]);
    tau_d_saturated[i] = tau_J_d[i] + std::max(std::min(difference, delta_tau_max), -delta_tau_max);
  }
  return tau_d_saturated;
}
<commit_msg>Fix saturation in the joint impedance example.<commit_after>// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include <array>
#include <atomic>
#include <cmath>
#include <functional>
#include <iostream>
#include <iterator>
#include <mutex>
#include <thread>

#include <franka/duration.h>
#include <franka/exception.h>
#include <franka/model.h>
#include <franka/robot.h>

namespace {
template <class T, size_t N>
std::ostream& operator<<(std::ostream& ostream, const std::array<T, N>& array) {
  ostream << "[";
  std::copy(array.cbegin(), array.cend() - 1, std::ostream_iterator<T>(ostream, ","));
  std::copy(array.cend() - 1, array.cend(), std::ostream_iterator<T>(ostream));
  ostream << "]";
  return ostream;
}
}  // anonymous namespace

/**
 * @example joint_impedance_control.cpp
 * An example showing a joint impedance type control that executes a Cartesian motion in the shape
 * of a circle. The example illustrates how to use the internal inverse kinematics to map a
 * Cartesian trajectory to joint space. The joint space target is tracked by an impedance control
 * that additionally compensates coriolis terms using the libfranka model library. This example also
 * serves to compare commanded vs. measured torques. The results are printed from a separate thread
 * to avoid blocking print functions in the real-time loop.
 */

std::array<double, 7> saturateTorqueRate(
    const double delta_tau_max,
    const std::array<double, 7>& tau_d_calculated,
    const std::array<double, 7>& tau_J_d,  // NOLINT (readability-identifier-naming)
    const std::array<double, 7>& gravity);

int main(int argc, char** argv) {
  // Check whether the required arguments were passed.
  if (argc != 5) {
    std::cerr << "Usage: ./" << argv[0] << " <robot-hostname>"
              << " <radius in [m]>"
              << " <vel_max in [m/s]>"
              << " <print_rate in [Hz]>" << std::endl;
    return -1;
  }

  // Set and initialize trajectory parameters.
  const double radius = std::stod(argv[2]);
  const double vel_max = std::stod(argv[3]);
  const double acceleration_time = 2.0;
  double vel_current = 0.0;
  double angle = 0.0;

  double time = 0.0;
  const double run_time = 20.0;

  // Set print rate for comparing commanded vs. measured torques.
  double print_rate = std::stod(argv[4]);
  if (print_rate < 0.0) {
    std::cerr << "print_rate too small, must be >= 0.0" << std::endl;
    return -1;
  }

  // Initialize data fields for the print thread.
  struct {
    std::mutex mutex;
    bool has_data;
    std::array<double, 7> tau_d_last;
    franka::RobotState robot_state;
    std::array<double, 7> gravity;
  } print_data{};
  std::atomic_bool running{true};

  // Start print thread.
  std::thread print_thread([print_rate, &print_data, &running]() {
    while (running) {
      // Sleep to achieve the desired print rate.
      std::this_thread::sleep_for(
          std::chrono::milliseconds(static_cast<int>((1.0 / print_rate * 1000.0))));

      // Try to lock data to avoid read write collisions.
      if (print_data.mutex.try_lock() && print_data.has_data) {
        std::array<double, 7> tau_error{};
        double error_rms(0.0);
        std::array<double, 7> tau_d_actual{};
        for (size_t i = 0; i < 7; ++i) {
          tau_d_actual[i] = print_data.tau_d_last[i] + print_data.gravity[i];
          tau_error[i] = tau_d_actual[i] - print_data.robot_state.tau_J[i];
          error_rms += std::sqrt(std::pow(tau_error[i], 2.0)) / tau_error.size();
        }
        // Print data to console
        std::cout << "tau_error [Nm]: " << tau_error << std::endl
                  << "tau_commanded [Nm]: " << tau_d_actual << std::endl
                  << "tau_measured [Nm]: " << print_data.robot_state.tau_J << std::endl
                  << "root mean square of tau_error [Nm]: " << error_rms << std::endl
                  << "-----------------------" << std::endl;
        print_data.has_data = false;
        print_data.mutex.unlock();
      }
    }
  });

  try {
    // Connect to robot.
    franka::Robot robot(argv[1]);

    // Set collision behavior.
    robot.setCollisionBehavior(
        {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}},
        {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}});

    // Load the kinematics and dynamics model.
    franka::Model model = robot.loadModel();

    // Read the initial pose to start the motion from there.
    std::array<double, 16> initial_pose = robot.readOnce().O_T_EE;

    // Define callback function to send Cartesian pose goals to get inverse kinematics solved.
    std::function<franka::CartesianPose(const franka::RobotState&, franka::Duration)>
        cartesian_pose_callback = [=, &time, &vel_current, &running, &angle](
            const franka::RobotState& /*state*/, franka::Duration period) -> franka::CartesianPose {
      // Update time.
      time += period.toSec();

      // Compute Cartesian velocity.
      if (vel_current < vel_max && time < run_time) {
        vel_current += period.toSec() * std::fabs(vel_max / acceleration_time);
      }
      if (vel_current > 0.0 && time > run_time) {
        vel_current -= period.toSec() * std::fabs(vel_max / acceleration_time);
      }
      vel_current = std::fmax(vel_current, 0.0);
      vel_current = std::fmin(vel_current, vel_max);

      // Compute new angle for our circular trajectory.
      angle += period.toSec() * vel_current / std::fabs(radius);
      if (angle > 2 * M_PI) {
        angle -= 2 * M_PI;
      }

      // Compute relative y and z positions of desired pose.
      double delta_y = radius * (1 - std::cos(angle));
      double delta_z = radius * std::sin(angle);
      franka::CartesianPose pose_desired = initial_pose;
      pose_desired.O_T_EE[13] += delta_y;
      pose_desired.O_T_EE[14] += delta_z;

      // Send desired pose.
      if (time >= run_time + acceleration_time) {
        running = false;
        return franka::MotionFinished(pose_desired);
      }

      return pose_desired;
    };

    // Set gains for the joint impedance control.
    // Stiffness
    const std::array<double, 7> k_gains = {{600.0, 600.0, 600.0, 600.0, 250.0, 150.0, 50.0}};
    // Damping
    const std::array<double, 7> d_gains = {{50.0, 50.0, 50.0, 50.0, 30.0, 25.0, 15.0}};

    // Maximum torque difference.
    const double delta_tau_max = 1.0;

    // Define callback for the joint torque control loop.
    std::function<franka::Torques(const franka::RobotState&, franka::Duration)>
        impedance_control_callback = [&print_data, &model, k_gains, d_gains, delta_tau_max](
            const franka::RobotState& state, franka::Duration /*period*/) -> franka::Torques {
      // Read current coriolis terms from model.
      std::array<double, 7> coriolis = model.coriolis(
          state, {{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}}, 0.0, {{0.0, 0.0, 0.0}});

      // Compute torque command from joint impedance control law.
      // Note: The answer to our Cartesian pose inverse kinematics is always in state.q_d with one
      // time step delay.
      std::array<double, 7> tau_d_calculated;
      for (size_t i = 0; i < 7; i++) {
        tau_d_calculated[i] =
            k_gains[i] * (state.q_d[i] - state.q[i]) - d_gains[i] * state.dq[i] + coriolis[i];
      }

      std::array<double, 7> tau_d_saturated =
          saturateTorqueRate(delta_tau_max, tau_d_calculated, state.tau_J_d,
                             model.gravity(state, 0.0, {{0.0, 0.0, 0.0}}));

      // Update data to print.
      if (print_data.mutex.try_lock()) {
        print_data.has_data = true;
        print_data.robot_state = state;
        print_data.tau_d_last = tau_d_saturated;
        print_data.gravity = model.gravity(state, 0.0, {{0.0, 0.0, 0.0}});
        print_data.mutex.unlock();
      }

      // Send torque command.
      return tau_d_saturated;
    };

    // Start real-time control loop.
    robot.control(impedance_control_callback, cartesian_pose_callback);

  } catch (const franka::Exception& ex) {
    running = false;
    std::cerr << ex.what() << std::endl;
  }

  if (print_thread.joinable()) {
    print_thread.join();
  }
  return 0;
}

std::array<double, 7> saturateTorqueRate(
    const double delta_tau_max,
    const std::array<double, 7>& tau_d_calculated,
    const std::array<double, 7>& tau_J_d,  // NOLINT (readability-identifier-naming)
    const std::array<double, 7>& gravity) {
  std::array<double, 7> tau_d_saturated{};
  for (size_t i = 0; i < 7; i++) {
    double difference = tau_d_calculated[i] - (tau_J_d[i] - gravity[i]);
    tau_d_saturated[i] =
        (tau_J_d[i] - gravity[i]) + std::max(std::min(difference, delta_tau_max), -delta_tau_max);
  }
  return tau_d_saturated;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N, M;
typedef tuple<int, int> takahashi;
vector<takahashi> V;

int main () {
  cin >> N >> M;
  for (auto i = 0; i < N; ++i) {
    int L, R;
    cin >> L >> R;
    V.push_back(takahashi(L, R));
  }
  sort(V.begin(), V.end());
  priority_queue<int, vector<int>, greater<int> > Q;
  vector<int> W;
  int ind = 1;
  for (auto x : V) {
    if (ind <= get<0>(x)) {
      ++ind;
    } else {
      Q.push(get<1>(x));
      W.push_back(Q.top());
      Q.pop();
    }
  }
  for (auto it = W.begin(); it != W.end(); ++it) {
    *it = M+1 - *it;
  }
  sort(W.begin(), W.end());
  ind = 1;
  int ans = 0;
  for (auto x : W) {
    if (ind <= x) {
      ++ind;
    } else {
      ++ans;
    }
  }
  cout << ans << endl;
}

<commit_msg>tried F.cpp to 'F'<commit_after>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N, M;
typedef tuple<int, int> takahashi;
vector<takahashi> V;

int main () {
  cin >> N >> M;
  for (auto i = 0; i < N; ++i) {
    int L, R;
    cin >> L >> R;
    V.push_back(takahashi(L, R));
  }
  sort(V.begin(), V.end());
  priority_queue<int, vector<int>, greater<int> > Q;
  vector<int> W;
  int ind = 1;
  for (auto x : V) {
    if (ind <= get<0>(x)) {
      ++ind;
    } else {
      Q.push(get<1>(x));
      W.push_back(Q.top());
      Q.pop();
    }
  }
  for (auto it = W.begin(); it != W.end(); ++it) {
    *it = M+1 - *it;
  }
  sort(W.begin(), W.end());
  for (auto x : W) {
    cerr << x << " ";
  }
  cerr << endl;
  ind = 1;
  int ans = 0;
  for (auto x : W) {
    if (ind <= x) {
      ++ind;
    } else {
      ++ans;
    }
  }
  cout << ans << endl;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N, K;
int cnt[2000][2000][2];
int sum[2000][2000][2];

int main () {
  fill(&cnt[0][0][0], &cnt[0][0][0]+2000*2000*2, 0);
  cin >> N >> K;
  int white = 0;
  for (auto i = 0; i < N; ++i) {
    int x, y;
    char c;
    cin >> x >> y >> c;
    bool color = (c == 'W');
    int area = ((x/K) + (y/K))%2;
    x %= K;
    y %= K;
    if (color) {
      white++;
      cnt[x + K * area][y][0]++;
      cnt[x + K * area][y + K][0]++;
    } else {
      cnt[x + K * area][y][1]++;
      cnt[x + K * area][y + K][1]++;
    }
  }
  for (auto k = 0; k < 2; ++k) {
    cerr << "k = " << k << endl;
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K; ++j) {
        cerr << cnt[i][j][k] << " ";
      }
      cerr << endl;
    }
  }
  for (auto k = 0; k < 2; ++k) {
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K; ++j) {
        sum[i][j][k] = cnt[i][j][k];
      }
    }
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K-1; ++j) {
        sum[i][j+1][k] += sum[i][j][k];
      }
    }
    for (auto j = 0; j < 2 * K; ++j) {
      for (auto i = 0; i < 2 * K-1; ++i) {
        sum[i+1][j][k] += sum[i][j][k];
      }
    }
  }
  for (auto k = 0; k < 2; ++k) {
    cerr << "k = " << k << endl;
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K; ++j) {
        cerr << sum[i][j][k] << " ";
      }
      cerr << endl;
    }
  }
  int ans = 0;
  for (auto i = 0; i < K; ++i) {
    for (auto j = 0; j < K; ++j) {
      int x = i + K;
      int y = j + K;
      int w = sum[x][y][0] - sum[i][y][0] - sum[x][j][0] + sum[i][j][0];
      int b = sum[x][y][1] - sum[i][y][1] - sum[x][j][1] + sum[i][j][1];
      cerr << "(" << x << ", " << y << ") = ("
           << w << ", " << b << ")" << endl;
      int c = white - w + b;
      ans = max(ans, c);
    }
  }
  cout << ans << endl;
}
<commit_msg>tried D.cpp to 'D'<commit_after>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N, K;
int cnt[2000][2000][2];
int sum[2000][2000][2];

int main () {
  fill(&cnt[0][0][0], &cnt[0][0][0]+2000*2000*2, 0);
  cin >> N >> K;
  int white = 0;
  for (auto i = 0; i < N; ++i) {
    int x, y;
    char c;
    cin >> x >> y >> c;
    bool color = (c == 'W');
    int area = ((x/K) + (y/K))%2;
    x %= K;
    y %= K;
    if (color) {
      white++;
      cnt[x + K * area][y][0]++;
      cnt[x + K * (1-area)][y + K][0]++;
    } else {
      cnt[x + K * area][y][1]++;
      cnt[x + K * (1-area)][y + K][1]++;
    }
  }
  for (auto k = 0; k < 2; ++k) {
    cerr << "k = " << k << endl;
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K; ++j) {
        cerr << cnt[i][j][k] << " ";
      }
      cerr << endl;
    }
  }
  for (auto k = 0; k < 2; ++k) {
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K; ++j) {
        sum[i][j][k] = cnt[i][j][k];
      }
    }
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K-1; ++j) {
        sum[i][j+1][k] += sum[i][j][k];
      }
    }
    for (auto j = 0; j < 2 * K; ++j) {
      for (auto i = 0; i < 2 * K-1; ++i) {
        sum[i+1][j][k] += sum[i][j][k];
      }
    }
  }
  for (auto k = 0; k < 2; ++k) {
    cerr << "k = " << k << endl;
    for (auto i = 0; i < 2 * K; ++i) {
      for (auto j = 0; j < 2 * K; ++j) {
        cerr << sum[i][j][k] << " ";
      }
      cerr << endl;
    }
  }
  int ans = 0;
  for (auto i = 0; i < K; ++i) {
    for (auto j = 0; j < K; ++j) {
      int x = i + K;
      int y = j + K;
      int w = sum[x][y][0] - sum[i][y][0] - sum[x][j][0] + sum[i][j][0];
      int b = sum[x][y][1] - sum[i][y][1] - sum[x][j][1] + sum[i][j][1];
      cerr << "(" << x << ", " << y << ") = ("
           << w << ", " << b << ")" << endl;
      int c = white - w + b;
      ans = max(ans, c);
    }
  }
  cout << ans << endl;
}
<|endoftext|>


<commit_before>#include "../ref/main_types.h"
#include "../src/glresource_types.hpp"
#include "../src/gltexturing.hpp"
#include "../src/gldebug.hpp"
#include "../src/glshaders.hpp"

#include <micros/api.h>

#include "stb_perlin.h"
#include "stb_image_write.h"

#include <GL/glew.h>

#include <vector>
#include <future>

static std::string dirname(std::string path)
{
        return path.substr(0, path.find_last_of("/\\"));
}

namespace
{
class RootDirFileSystem : public FileSystem
{
public:
        RootDirFileSystem(std::string const& basePath) : base_path(basePath) {}

        std::ifstream open_file(std::string relpath) const
        {
                auto stream = std::ifstream(base_path + "/" + relpath);

                if (stream.fail()) {
                        throw std::runtime_error("could not load file at " + relpath);
                }

                return stream;
        }

private:
        std::string const base_path;
};

class Tasks : public DisplayThreadTasks
{
public:
        void add_task(std::function<bool()>&& task)
        {
                std::lock_guard<std::mutex> lock(tasks_mtx);
                tasks.emplace_back(task);
        }

        void run()
        {
                std::lock_guard<std::mutex> lock(tasks_mtx);
                for (auto& task : tasks) {
                        std::future<bool> future = task.get_future();
                        task();
                        future.get();
                }
                tasks.clear();
        }

private:
        std::mutex tasks_mtx;
        std::vector<std::packaged_task<bool()>> tasks;
};
}

extern void render_next_2chn_48khz_audio(uint64_t time_micros,
                int const sample_count, double left[/*sample_count*/],
                double right[/*sample_count*/])
{
        // silence is soothing
}

struct SimpleShaderProgram : public ShaderProgramResource {
        VertexShaderResource vertexShader;
        FragmentShaderResource fragmentShader;
};

static void defineProgram(SimpleShaderProgram& program,
                          std::string const& vertexShaderSource,
                          std::string const& fragmentShaderSource)
{
        compile(program.vertexShader, vertexShaderSource);
        compile(program.fragmentShader, fragmentShaderSource);

        glAttachShader(program.id, program.vertexShader.id);
        glAttachShader(program.id, program.fragmentShader.id);
        glLinkProgram(program.id);
}

struct MainShaderVariables {
        GLint textureUniforms[2];
        GLint positionAttrib;
        GLint texcoordAttrib;
};

static MainShaderVariables getMainShaderVariables(ShaderProgramResource const&
                program)
{
        auto const id = program.id;
        MainShaderVariables variables = {
                {
                        glGetUniformLocation(id, "tex0"),
                        glGetUniformLocation(id, "tex1")
                },
                glGetAttribLocation(program.id, "position"),
                glGetAttribLocation(program.id, "texcoord")
        };

        return variables;
}

static size_t define2dQuadIndices(BufferResource const& buffer)
{
        size_t count = 0;
        withElementBuffer(buffer,
        [&count]() {
                GLuint data[] = {
                        0, 1, 2, 2, 3, 0,
                };
                count = sizeof data / sizeof data[0];

                glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof data, data,
                             GL_STREAM_DRAW);
        });

        return count;
}

static GLvoid* define2dQuadBuffer(BufferResource const& buffer, float xmin,
                                  float ymin, float width, float height)
{
        withArrayBuffer(buffer,
        [=]() {
                float data[] = {
                        xmin, ymin,
                        xmin, ymin + height,
                        xmin + width, ymin + height,
                        xmin + width, ymin,
                };

                glBufferData(GL_ARRAY_BUFFER, sizeof data, data, GL_STREAM_DRAW);
        });

        return 0;
}

static void define2dTrianglesVertexArray(BufferResource const& indices,
                GLint positionAttrib, BufferResource const& positions,
                GLvoid* positionsOffset,
                GLint texcoordAttrib, BufferResource const& texcoords,
                GLvoid* texcoordsOffset)
{
        glBindBuffer(GL_ARRAY_BUFFER, texcoords.id);
        glVertexAttribPointer(texcoordAttrib, 2, GL_FLOAT, GL_FALSE, 0,
                              texcoordsOffset);

        glBindBuffer(GL_ARRAY_BUFFER, positions.id);
        glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0,
                              positionsOffset);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices.id);
        glEnableVertexAttribArray(texcoordAttrib);
        glEnableVertexAttribArray(positionAttrib);
}

static
void perlinNoisePixelFiller (uint32_t* data, int width, int height)
{
        for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                        uint8_t values[4];
                        for (size_t i = 0; i < sizeof values / sizeof values[0]; i++) {
                                float val = 0.5 + stb_perlin_noise3(13.0 * x / width, 17.0 * y / height, 0.0);
                                if (val > 1.0) {
                                        val = 1.0;
                                } else if (val < 0.0) {
                                        val = 0.0;
                                }

                                values[i] = (int) (255 * val) & 0xff;
                        }

                        data[x + y*width] = (values[0] << 24)
                                            | (values[1] << 16)
                                            | (values[2] << 8)
                                            | values[3];
                }
        }
}

extern void render(uint64_t time_micros)
{
        // infrastructure
        static Tasks tasks;
        static RootDirFileSystem codeBaseFS { dirname(__FILE__) };
        static FileLoaderResource fileLoader = makeFileLoader(codeBaseFS, tasks);

        tasks.run();

        // caches
        struct FileContent {
                int state;
                std::string path;
                std::string content;
        };
        struct FileHandle {
                long id;
        };
        static auto loadingFiles = std::vector<FileContent> {};
        static auto loadedFiles = std::vector<FileContent> {};
        auto loadFileContent = [](std::string const& path) -> FileHandle {
                auto lookupFC = [](std::vector<FileContent>& cache,
                std::string const& path) -> FileContent* {
                        auto existing = std::find_if(std::begin(cache), std::end(cache), [=](FileContent const& fc)
                        {
                                return path == fc.path;
                        });
                        if (existing == std::end(cache))
                        {
                                return nullptr;
                        }

                        return &(*existing);
                };

                auto loaded = lookupFC(loadedFiles, path);
                if (loaded)
                {
                        return { .id = loaded - &loadedFiles.front() };
                }

                auto loading = lookupFC(loadingFiles, path);
                if (!loading)
                {
                        loadingFiles.push_back({ .state = -1, .path = path });
                        loadFile(*fileLoader.get(), path,
                        [=](std::string const& content) {
                                auto loading = lookupFC(loadingFiles, path);
                                loading->content = content;
                                loading->state = 0;
                        });
                        return { .id = -1 };
                }

                if (loading->state == 0)
                {
                        loadedFiles.push_back(*loading);
                        return { .id = static_cast<long>(loadedFiles.size() - 1) };
                }

                return { .id = -1 };
        };

        // prototype for a new render code

        // value types
        struct VertexShader {
                std::string source;
        };
        struct FragmentShader {
                std::string source;
        };
        struct Program {
                VertexShader vertexShader;
                FragmentShader fragmentShader;
        };
        struct Texture {
                int width = 0;
                int height = 0;
                void (*pixelFiller)(uint32_t* pixels, int width, int height) = nullptr;
        };

        struct ProgramInputs {
                std::vector<Texture> textures;
        };
        struct Geometry {};
        struct Rect {
                float x;
                float y;
                float width;
                float height;
        };

        // constructors
        auto texture = [](int width, int height,
        std::function<void(uint32_t*, int, int)>) {
                return Texture {};
        };

        auto vertexShaderFromFile = [=](std::string filename) {
                auto fh = loadFileContent(filename);
                if (fh.id < 0) {
                        return VertexShader {};
                }

                return VertexShader { .source = loadedFiles[fh.id].content };
        };

        auto fragmentShaderFromFile = [=](std::string filename) {
                auto fh = loadFileContent(filename);
                if (fh.id < 0) {
                        return FragmentShader {};
                }

                return FragmentShader { .source = loadedFiles[fh.id].content };
        };

        // drawing infrastructure

        // persistent datastructure... the core of the infrastructure
        class FrameSeries
        {
        public:
                ~FrameSeries()
                {
                        printf("summary:\n");
                        printf("program creations: %ld\n", programCreations);
                        printf("texture creations: %ld\n", textureCreations);
                }

                void beginFrame()
                {
                        fragmentShaders.clear();
                        vertexShaders.clear();
                        programs.clear();
                }

                TextureResource& texture(Texture textureDef)
                {
                        auto existing =
                                std::find_if(std::begin(textureDefs), std::end(textureDefs),
                        [&textureDef](Texture const& element) {
                                return element.width == textureDef.width
                                       && element.height == textureDef.height
                                       && element.pixelFiller == textureDef.pixelFiller;
                        });
                        if (existing != std::end(textureDefs)) {
                                return textures[&(*existing) - &textureDefs.front()];
                        }

                        textures.emplace_back();
                        textureDefs.emplace_back();

                        auto& texture = textures.back();
                        withTexture(texture,
                        [&textureDef]() {
                                defineNonMipmappedARGB32Texture(textureDef.width, textureDef.height,
                                                                textureDef.pixelFiller);
                        });

                        textureCreations++;
                        return texture;
                }

                ShaderProgramResource& program(Program programDef)
                {
                        auto existing =
                                std::find_if(std::begin(programDefs), std::end(programDefs),
                        [&programDef](Program const& element) {
                                return element.fragmentShader.source
                                       == programDef.fragmentShader.source
                                       && element.vertexShader.source
                                       == programDef.vertexShader.source;
                        });
                        if (existing != std::end(programDefs)) {
                                return programs[&(*existing) - &programDefs.front()];
                        }

                        programs.emplace_back();
                        programDefs.push_back(programDef);
                        vertexShaders.emplace_back();
                        fragmentShaders.emplace_back();

                        auto& program = programs.back();
                        auto& vertexShader = vertexShaders.back();
                        auto& fragmentShader = fragmentShaders.back();

                        compile(vertexShader, programDef.vertexShader.source);
                        compile(fragmentShader, programDef.fragmentShader.source);

                        glAttachShader(program.id, vertexShader.id);
                        glAttachShader(program.id, fragmentShader.id);
                        glLinkProgram(program.id);

                        programCreations++;

                        return program;
                }

        private:
                std::vector<TextureResource> textures;
                std::vector<Texture> textureDefs;
                long textureCreations = {0};

                std::vector<VertexShaderResource> vertexShaders;
                std::vector<FragmentShaderResource> fragmentShaders;
                std::vector<ShaderProgramResource> programs;
                std::vector<Program> programDefs;
                long programCreations = {0};
        };

        auto draw = [](FrameSeries& output, Program programDef, ProgramInputs inputs,
        Geometry geometryDef) {
                output.beginFrame();

                if (programDef.vertexShader.source.empty()
                    || programDef.fragmentShader.source.empty()) {
                        return;
                }

                auto& program = output.program(programDef);
                withShaderProgram(program,
                [&output,&inputs,&program]() {
                        auto textureTargets = std::vector<GLenum> {};
                        {
                                auto i = 0;
                                for (auto& textureDef : inputs.textures) {
                                        auto& texture = output.texture(textureDef);
                                        auto target = GL_TEXTURE0 + i;
                                        i++;

                                        textureTargets.emplace_back(target);
                                        glActiveTexture(target);
                                        glBindTexture(GL_TEXTURE_2D, texture.id);
                                        i++;
                                }
                        }

                        validate(program);

                        // draw here

                        // unbind
                        for (auto target : textureTargets) {
                                glActiveTexture(GL_TEXTURE0 + target);
                                glBindTexture(GL_TEXTURE_2D, 0);
                        }
                });
        };

        // library
        auto quad = [](Rect coords, Rect uvcoords) -> Geometry {
                return Geometry {};
        };

        // user code

        static FrameSeries output;

        draw(output, {
                .vertexShader = vertexShaderFromFile("main.vs"),
                .fragmentShader = fragmentShaderFromFile("main.fs")
        }, {
                .textures = { texture(64, 64, perlinNoisePixelFiller) }
        },
        quad({ .x = 1.0, .y = -1.0, .width = 2.0, .height = 2.0 },
        { .x = 0.0, .y = 0.0, .width = 1.0, .height = 1.0 })
            );
}

extern void render_next_gl3(uint64_t time_micros)
{
        static Tasks tasks;
        static RootDirFileSystem codeBaseFS { dirname(__FILE__) };

        static struct Resources {
                Resources() :
                        fileLoader(makeFileLoader(codeBaseFS, tasks))
                {
                        {
                                quadIndicesOffset = 0;
                                quadIndicesCount = define2dQuadIndices(indices);
                                quadPositionOffset = define2dQuadBuffer(vertices, -1.0, -1.0, 2.0, 2.0);
                                quadTexcoordsOffset = define2dQuadBuffer(texcoords, 0.0, 0.0, 1.0, 1.0);
                        }

                        loadFilePair(*fileLoader.get(), "main.vs",
                        "main.fs", [=](std::string const& contentVS, std::string const& contentFS) {
                                defineProgram(mainShader, contentVS, contentFS);
                                mainShaderVars = getMainShaderVariables(mainShader);

                                withShaderProgram(mainShader, [=]() {
                                        // bind to texture units 0 and 1
                                        glUniform1i(mainShaderVars.textureUniforms[0], 0);
                                        glUniform1i(mainShaderVars.textureUniforms[1], 1);
                                });

                                withVertexArray(vertexArray, [=]() {
                                        define2dTrianglesVertexArray
                                        (indices,
                                         mainShaderVars.positionAttrib, vertices, quadPositionOffset,
                                         mainShaderVars.texcoordAttrib, texcoords, quadTexcoordsOffset);
                                        validate(mainShader);
                                });
                        });

                        {
                                int const width = 64;
                                int const height = 64;

                                withTexture(quadTexture,
                                []() {
                                        defineNonMipmappedARGB32Texture(width, height, perlinNoisePixelFiller);
                                });
                        }
                }

                SimpleShaderProgram mainShader;
                MainShaderVariables mainShaderVars;
                FileLoaderResource fileLoader;

                TextureResource quadTexture;
                VertexArrayResource vertexArray;
                BufferResource indices;
                BufferResource vertices;
                BufferResource texcoords;

                GLvoid* quadIndicesOffset;
                size_t quadIndicesCount;
                GLvoid* quadPositionOffset;
                GLvoid* quadTexcoordsOffset;
        } resources;

        tasks.run();

        render(time_micros);

        auto const& program = resources.mainShader;
        if (program.id > 0) {
                withShaderProgram(program, []() {
                        withTexture(resources.quadTexture, []() {
                                withVertexArray(resources.vertexArray, []() {
                                        glDrawElements(GL_TRIANGLES, resources.quadIndicesCount, GL_UNSIGNED_INT,
                                                       resources.quadIndicesOffset);
                                });
                        });
                });
        }
}

int main()
{
        runtime_init();

        return 0;
}
<commit_msg>disable incorrect frame begin recycling<commit_after>#include "../ref/main_types.h"
#include "../src/glresource_types.hpp"
#include "../src/gltexturing.hpp"
#include "../src/gldebug.hpp"
#include "../src/glshaders.hpp"

#include <micros/api.h>

#include "stb_perlin.h"
#include "stb_image_write.h"

#include <GL/glew.h>

#include <vector>
#include <future>

static std::string dirname(std::string path)
{
        return path.substr(0, path.find_last_of("/\\"));
}

namespace
{
class RootDirFileSystem : public FileSystem
{
public:
        RootDirFileSystem(std::string const& basePath) : base_path(basePath) {}

        std::ifstream open_file(std::string relpath) const
        {
                auto stream = std::ifstream(base_path + "/" + relpath);

                if (stream.fail()) {
                        throw std::runtime_error("could not load file at " + relpath);
                }

                return stream;
        }

private:
        std::string const base_path;
};

class Tasks : public DisplayThreadTasks
{
public:
        void add_task(std::function<bool()>&& task)
        {
                std::lock_guard<std::mutex> lock(tasks_mtx);
                tasks.emplace_back(task);
        }

        void run()
        {
                std::lock_guard<std::mutex> lock(tasks_mtx);
                for (auto& task : tasks) {
                        std::future<bool> future = task.get_future();
                        task();
                        future.get();
                }
                tasks.clear();
        }

private:
        std::mutex tasks_mtx;
        std::vector<std::packaged_task<bool()>> tasks;
};
}

extern void render_next_2chn_48khz_audio(uint64_t time_micros,
                int const sample_count, double left[/*sample_count*/],
                double right[/*sample_count*/])
{
        // silence is soothing
}

struct SimpleShaderProgram : public ShaderProgramResource {
        VertexShaderResource vertexShader;
        FragmentShaderResource fragmentShader;
};

static void defineProgram(SimpleShaderProgram& program,
                          std::string const& vertexShaderSource,
                          std::string const& fragmentShaderSource)
{
        compile(program.vertexShader, vertexShaderSource);
        compile(program.fragmentShader, fragmentShaderSource);

        glAttachShader(program.id, program.vertexShader.id);
        glAttachShader(program.id, program.fragmentShader.id);
        glLinkProgram(program.id);
}

struct MainShaderVariables {
        GLint textureUniforms[2];
        GLint positionAttrib;
        GLint texcoordAttrib;
};

static MainShaderVariables getMainShaderVariables(ShaderProgramResource const&
                program)
{
        auto const id = program.id;
        MainShaderVariables variables = {
                {
                        glGetUniformLocation(id, "tex0"),
                        glGetUniformLocation(id, "tex1")
                },
                glGetAttribLocation(program.id, "position"),
                glGetAttribLocation(program.id, "texcoord")
        };

        return variables;
}

static size_t define2dQuadIndices(BufferResource const& buffer)
{
        size_t count = 0;
        withElementBuffer(buffer,
        [&count]() {
                GLuint data[] = {
                        0, 1, 2, 2, 3, 0,
                };
                count = sizeof data / sizeof data[0];

                glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof data, data,
                             GL_STREAM_DRAW);
        });

        return count;
}

static GLvoid* define2dQuadBuffer(BufferResource const& buffer, float xmin,
                                  float ymin, float width, float height)
{
        withArrayBuffer(buffer,
        [=]() {
                float data[] = {
                        xmin, ymin,
                        xmin, ymin + height,
                        xmin + width, ymin + height,
                        xmin + width, ymin,
                };

                glBufferData(GL_ARRAY_BUFFER, sizeof data, data, GL_STREAM_DRAW);
        });

        return 0;
}

static void define2dTrianglesVertexArray(BufferResource const& indices,
                GLint positionAttrib, BufferResource const& positions,
                GLvoid* positionsOffset,
                GLint texcoordAttrib, BufferResource const& texcoords,
                GLvoid* texcoordsOffset)
{
        glBindBuffer(GL_ARRAY_BUFFER, texcoords.id);
        glVertexAttribPointer(texcoordAttrib, 2, GL_FLOAT, GL_FALSE, 0,
                              texcoordsOffset);

        glBindBuffer(GL_ARRAY_BUFFER, positions.id);
        glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0,
                              positionsOffset);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices.id);
        glEnableVertexAttribArray(texcoordAttrib);
        glEnableVertexAttribArray(positionAttrib);
}

static
void perlinNoisePixelFiller (uint32_t* data, int width, int height)
{
        for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                        uint8_t values[4];
                        for (size_t i = 0; i < sizeof values / sizeof values[0]; i++) {
                                float val = 0.5 + stb_perlin_noise3(13.0 * x / width, 17.0 * y / height, 0.0);
                                if (val > 1.0) {
                                        val = 1.0;
                                } else if (val < 0.0) {
                                        val = 0.0;
                                }

                                values[i] = (int) (255 * val) & 0xff;
                        }

                        data[x + y*width] = (values[0] << 24)
                                            | (values[1] << 16)
                                            | (values[2] << 8)
                                            | values[3];
                }
        }
}

extern void render(uint64_t time_micros)
{
        // infrastructure
        static Tasks tasks;
        static RootDirFileSystem codeBaseFS { dirname(__FILE__) };
        static FileLoaderResource fileLoader = makeFileLoader(codeBaseFS, tasks);

        tasks.run();

        // caches
        struct FileContent {
                int state;
                std::string path;
                std::string content;
        };
        struct FileHandle {
                long id;
        };
        static auto loadingFiles = std::vector<FileContent> {};
        static auto loadedFiles = std::vector<FileContent> {};
        auto loadFileContent = [](std::string const& path) -> FileHandle {
                auto lookupFC = [](std::vector<FileContent>& cache,
                std::string const& path) -> FileContent* {
                        auto existing = std::find_if(std::begin(cache), std::end(cache), [=](FileContent const& fc)
                        {
                                return path == fc.path;
                        });
                        if (existing == std::end(cache))
                        {
                                return nullptr;
                        }

                        return &(*existing);
                };

                auto loaded = lookupFC(loadedFiles, path);
                if (loaded)
                {
                        return { .id = loaded - &loadedFiles.front() };
                }

                auto loading = lookupFC(loadingFiles, path);
                if (!loading)
                {
                        loadingFiles.push_back({ .state = -1, .path = path });
                        loadFile(*fileLoader.get(), path,
                        [=](std::string const& content) {
                                auto loading = lookupFC(loadingFiles, path);
                                loading->content = content;
                                loading->state = 0;
                        });
                        return { .id = -1 };
                }

                if (loading->state == 0)
                {
                        loadedFiles.push_back(*loading);
                        return { .id = static_cast<long>(loadedFiles.size() - 1) };
                }

                return { .id = -1 };
        };

        // prototype for a new render code

        // value types
        struct VertexShader {
                std::string source;
        };
        struct FragmentShader {
                std::string source;
        };
        struct Program {
                VertexShader vertexShader;
                FragmentShader fragmentShader;
        };
        struct Texture {
                int width = 0;
                int height = 0;
                void (*pixelFiller)(uint32_t* pixels, int width, int height) = nullptr;
        };

        struct ProgramInputs {
                std::vector<Texture> textures;
        };
        struct Geometry {};
        struct Rect {
                float x;
                float y;
                float width;
                float height;
        };

        // constructors
        auto texture = [](int width, int height,
        std::function<void(uint32_t*, int, int)>) {
                return Texture {};
        };

        auto vertexShaderFromFile = [=](std::string filename) {
                auto fh = loadFileContent(filename);
                if (fh.id < 0) {
                        return VertexShader {};
                }

                return VertexShader { .source = loadedFiles[fh.id].content };
        };

        auto fragmentShaderFromFile = [=](std::string filename) {
                auto fh = loadFileContent(filename);
                if (fh.id < 0) {
                        return FragmentShader {};
                }

                return FragmentShader { .source = loadedFiles[fh.id].content };
        };

        // drawing infrastructure

        // persistent datastructure... the core of the infrastructure
        class FrameSeries
        {
        public:
                ~FrameSeries()
                {
                        printf("summary:\n");
                        printf("program creations: %ld\n", programCreations);
                        printf("texture creations: %ld\n", textureCreations);
                }

                void beginFrame()
                {
                        // we should invalidate arrays so as to garbage
                        // collect / recycle the now un-needed definitions
                }
                }

                TextureResource& texture(Texture textureDef)
                {
                        auto existing =
                                std::find_if(std::begin(textureDefs), std::end(textureDefs),
                        [&textureDef](Texture const& element) {
                                return element.width == textureDef.width
                                       && element.height == textureDef.height
                                       && element.pixelFiller == textureDef.pixelFiller;
                        });
                        if (existing != std::end(textureDefs)) {
                                return textures[&(*existing) - &textureDefs.front()];
                        }

                        textures.emplace_back();
                        textureDefs.emplace_back();

                        auto& texture = textures.back();
                        withTexture(texture,
                        [&textureDef]() {
                                defineNonMipmappedARGB32Texture(textureDef.width, textureDef.height,
                                                                textureDef.pixelFiller);
                        });

                        textureCreations++;
                        return texture;
                }

                ShaderProgramResource& program(Program programDef)
                {
                        auto existing =
                                std::find_if(std::begin(programDefs), std::end(programDefs),
                        [&programDef](Program const& element) {
                                return element.fragmentShader.source
                                       == programDef.fragmentShader.source
                                       && element.vertexShader.source
                                       == programDef.vertexShader.source;
                        });
                        if (existing != std::end(programDefs)) {
                                return programs[&(*existing) - &programDefs.front()];
                        }

                        programs.emplace_back();
                        programDefs.push_back(programDef);
                        vertexShaders.emplace_back();
                        fragmentShaders.emplace_back();

                        auto& program = programs.back();
                        auto& vertexShader = vertexShaders.back();
                        auto& fragmentShader = fragmentShaders.back();

                        compile(vertexShader, programDef.vertexShader.source);
                        compile(fragmentShader, programDef.fragmentShader.source);

                        glAttachShader(program.id, vertexShader.id);
                        glAttachShader(program.id, fragmentShader.id);
                        glLinkProgram(program.id);

                        programCreations++;

                        return program;
                }

        private:
                std::vector<TextureResource> textures;
                std::vector<Texture> textureDefs;
                long textureCreations = {0};

                std::vector<VertexShaderResource> vertexShaders;
                std::vector<FragmentShaderResource> fragmentShaders;
                std::vector<ShaderProgramResource> programs;
                std::vector<Program> programDefs;
                long programCreations = {0};
        };

        auto draw = [](FrameSeries& output, Program programDef, ProgramInputs inputs,
        Geometry geometryDef) {
                output.beginFrame();

                if (programDef.vertexShader.source.empty()
                    || programDef.fragmentShader.source.empty()) {
                        return;
                }

                auto& program = output.program(programDef);
                withShaderProgram(program,
                [&output,&inputs,&program]() {
                        auto textureTargets = std::vector<GLenum> {};
                        {
                                auto i = 0;
                                for (auto& textureDef : inputs.textures) {
                                        auto& texture = output.texture(textureDef);
                                        auto target = GL_TEXTURE0 + i;
                                        i++;

                                        textureTargets.emplace_back(target);
                                        glActiveTexture(target);
                                        glBindTexture(GL_TEXTURE_2D, texture.id);
                                        i++;
                                }
                        }

                        validate(program);

                        // draw here

                        // unbind
                        for (auto target : textureTargets) {
                                glActiveTexture(GL_TEXTURE0 + target);
                                glBindTexture(GL_TEXTURE_2D, 0);
                        }
                });
        };

        // library
        auto quad = [](Rect coords, Rect uvcoords) -> Geometry {
                return Geometry {};
        };

        // user code

        static FrameSeries output;

        draw(output, {
                .vertexShader = vertexShaderFromFile("main.vs"),
                .fragmentShader = fragmentShaderFromFile("main.fs")
        }, {
                .textures = { texture(64, 64, perlinNoisePixelFiller) }
        },
        quad({ .x = 1.0, .y = -1.0, .width = 2.0, .height = 2.0 },
        { .x = 0.0, .y = 0.0, .width = 1.0, .height = 1.0 })
            );
}

extern void render_next_gl3(uint64_t time_micros)
{
        static Tasks tasks;
        static RootDirFileSystem codeBaseFS { dirname(__FILE__) };

        static struct Resources {
                Resources() :
                        fileLoader(makeFileLoader(codeBaseFS, tasks))
                {
                        {
                                quadIndicesOffset = 0;
                                quadIndicesCount = define2dQuadIndices(indices);
                                quadPositionOffset = define2dQuadBuffer(vertices, -1.0, -1.0, 2.0, 2.0);
                                quadTexcoordsOffset = define2dQuadBuffer(texcoords, 0.0, 0.0, 1.0, 1.0);
                        }

                        loadFilePair(*fileLoader.get(), "main.vs",
                        "main.fs", [=](std::string const& contentVS, std::string const& contentFS) {
                                defineProgram(mainShader, contentVS, contentFS);
                                mainShaderVars = getMainShaderVariables(mainShader);

                                withShaderProgram(mainShader, [=]() {
                                        // bind to texture units 0 and 1
                                        glUniform1i(mainShaderVars.textureUniforms[0], 0);
                                        glUniform1i(mainShaderVars.textureUniforms[1], 1);
                                });

                                withVertexArray(vertexArray, [=]() {
                                        define2dTrianglesVertexArray
                                        (indices,
                                         mainShaderVars.positionAttrib, vertices, quadPositionOffset,
                                         mainShaderVars.texcoordAttrib, texcoords, quadTexcoordsOffset);
                                        validate(mainShader);
                                });
                        });

                        {
                                int const width = 64;
                                int const height = 64;

                                withTexture(quadTexture,
                                []() {
                                        defineNonMipmappedARGB32Texture(width, height, perlinNoisePixelFiller);
                                });
                        }
                }

                SimpleShaderProgram mainShader;
                MainShaderVariables mainShaderVars;
                FileLoaderResource fileLoader;

                TextureResource quadTexture;
                VertexArrayResource vertexArray;
                BufferResource indices;
                BufferResource vertices;
                BufferResource texcoords;

                GLvoid* quadIndicesOffset;
                size_t quadIndicesCount;
                GLvoid* quadPositionOffset;
                GLvoid* quadTexcoordsOffset;
        } resources;

        tasks.run();

        render(time_micros);

        auto const& program = resources.mainShader;
        if (program.id > 0) {
                withShaderProgram(program, []() {
                        withTexture(resources.quadTexture, []() {
                                withVertexArray(resources.vertexArray, []() {
                                        glDrawElements(GL_TRIANGLES, resources.quadIndicesCount, GL_UNSIGNED_INT,
                                                       resources.quadIndicesOffset);
                                });
                        });
                });
        }
}

int main()
{
        runtime_init();

        return 0;
}
<|endoftext|>