<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 "ink_defs.h"
#include "ink_thread.h"
#include "ink_inet.h"

#include <ts/TestBox.h>
#include <MgmtMarshall.h>
#include <MgmtSocket.h>

#define CHECK_EQ(expr, len) do { \
  MgmtMarshallInt rcvd = static_cast<MgmtMarshallInt>(expr); \
  box.check(rcvd == static_cast<MgmtMarshallInt>(len), "%s returned length %d, expected %d", #expr, rcvd, static_cast<MgmtMarshallInt>(len)); \
} while (0)

#define CHECK_VALUE(value, expect, fmt) do { \
  box.check((value) == (expect), "received marshalled value " fmt ", expected " fmt "", (value), (expect)); \
} while (0)

// The NULL string is marshalled the same as the empty string.
#define CHECK_STRING(value, expect) do { \
  if (value) { \
    box.check(strcmp((value), (expect)) == 0, "received marshalled value '%s', expected '%s'", (value), (expect)); \
  } else { \
    box.check(strcmp((expect), "") == 0, "received marshalled value '%s', expected ''", (expect)); \
  } \
} while (0)

const MgmtMarshallType inval[] = {
  (MgmtMarshallType)1568
};

const MgmtMarshallType ifields[] = {
  MGMT_MARSHALL_INT, MGMT_MARSHALL_LONG
};

const MgmtMarshallType sfields[] = {
  MGMT_MARSHALL_STRING,
};

const MgmtMarshallType dfields[] = {
  MGMT_MARSHALL_DATA,
};

const MgmtMarshallType afields[] = {
  MGMT_MARSHALL_DATA,
  MGMT_MARSHALL_INT,
  MGMT_MARSHALL_LONG,
  MGMT_MARSHALL_STRING,
  MGMT_MARSHALL_LONG,
  MGMT_MARSHALL_LONG,
};

const char alpha[] = "abcdefghijklmnopqrstuvwxyz0123456789";
const char * stringvals[] = {
  NULL,
  "",
  "randomstring"
};

static bool
errno_is_continue()
{
  return errno == EALREADY || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EAGAIN || mgmt_transient_error();
}

static int
message_connect_channel(RegressionTest * t, int listenfd, int clientfd, int serverport)
{
//  bool need_connect = true;
  bool need_accept = true;
  int serverfd = -1;

  struct sockaddr_in in;

  ink_zero(in);
  in.sin_family = AF_INET;
  in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  in.sin_port = htons(serverport);

  fcntl(clientfd, F_SETFL, O_NONBLOCK);
  fcntl(listenfd, F_SETFL, O_NONBLOCK);

  connect(clientfd, (const struct sockaddr *)&in, sizeof(in));

    while (need_accept) {
      serverfd = accept(listenfd, NULL, NULL);
      if (serverfd == -1) {
      rprintf(t, "accepting, %d %s\n", errno, strerror(errno));
        if (!errno_is_continue()) {
          return -1;
        }
        ink_thr_yield();
      } else {
        need_accept = false;
      }
    }

  return serverfd;
}

static int
message_listen(int& port)
{
  IpEndpoint sa;
  socklen_t slen;
  int fd;

  fd = mgmt_socket(AF_INET, SOCK_STREAM, 0);
  if (fd == -1) {
    goto fail;
  }

  sa.setToAnyAddr(AF_INET);
  if (bind(fd, &sa.sa, sizeof(sa)) == -1) {
    goto fail;
  }

  slen = sizeof(sa);
  if (getsockname(fd, &sa.sa, &slen) == -1) {
    goto fail;
  }

  port = ntohs(ats_ip_port_cast(&sa.sa));

  listen(fd, 5);
  return fd;

fail:
  close(fd);
  return -1;
}

REGRESSION_TEST(MessageReadWriteA)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus, REGRESSION_TEST_PASSED);

  int listenfd = -1;
  int serverfd = -1;
  int clientfd = -1;
  int serverport = 0;

  MgmtMarshallInt     mint = 0;
  MgmtMarshallLong    mlong = 0;
  MgmtMarshallString  mstring = NULL;
  MgmtMarshallData    mdata = { NULL, 0 };

  clientfd = mgmt_socket(AF_INET, SOCK_STREAM, 0);
  listenfd = message_listen(serverport);
  serverfd = message_connect_channel(t, listenfd, clientfd, serverport);
  rprintf(t, "listenfd=%d clientfd=%d, serverfd=%d, port=%d\n", listenfd, clientfd, serverfd, serverport);

  fcntl(clientfd, F_SETFL, O_NDELAY);
  fcntl(serverfd, F_SETFL, O_NDELAY);

  mint = 99;
  mlong = (MgmtMarshallLong)(&listenfd);

  // Check invalid Fd write
  CHECK_EQ(mgmt_message_write(5000, ifields, countof(ifields), &mint, &mlong), -1);

  CHECK_EQ(mgmt_message_write(clientfd, ifields, countof(ifields), &mint, &mlong), 12);

  mint = 0;
  mlong = 0;
  CHECK_EQ(mgmt_message_read(serverfd, ifields, countof(ifields), &mint, &mlong), 12);
  CHECK_VALUE(mint, 99, "%" PRId32);
  CHECK_VALUE(mlong, (MgmtMarshallLong)(&listenfd), "%" PRId64);

  // Marshall a string.
  for (unsigned i = 0; i < countof(stringvals); ++i) {
    const char * s = stringvals[i];
    size_t len = 4 /* length */ + (s ? strlen(s) : 0) /* bytes */ + 1 /* NULL */;

    mstring = s ? ats_strdup(s) : NULL;
    CHECK_EQ(mgmt_message_write(clientfd, sfields, countof(sfields), &mstring), len);
    ats_free(mstring); mstring = NULL;

    CHECK_EQ(mgmt_message_read(serverfd, sfields, countof(sfields), &mstring), len);
    CHECK_STRING(s, mstring);
    ats_free(mstring); mstring = NULL;
  }

  // Marshall data.
  mdata.ptr = ats_strdup(alpha);
  mdata.len = strlen(alpha);
  CHECK_EQ(mgmt_message_write(clientfd, dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  ats_free(mdata.ptr); ink_zero(mdata);

  CHECK_EQ(mgmt_message_read(serverfd, dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  CHECK_VALUE(mdata.len, strlen(alpha), "%zu");
  box.check(memcmp(mdata.ptr, alpha, strlen(alpha)) == 0, "unexpected mdata contents");
  ats_free(mdata.ptr); ink_zero(mdata);

  close(clientfd);
  close(listenfd);
  close(serverfd);
}

REGRESSION_TEST(MessageMarshall)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus, REGRESSION_TEST_PASSED);

  char msgbuf[4096];

  MgmtMarshallInt     mint = 0;
  MgmtMarshallLong    mlong = 0;
  MgmtMarshallString  mstring = NULL;
  MgmtMarshallData    mdata = { NULL, 0 };

  // Parse empty message.
  CHECK_EQ(mgmt_message_parse(NULL, 0, NULL, 0), 0);

  // Marshall empty message.
  CHECK_EQ(mgmt_message_marshall(NULL, 0, NULL, 0), 0);

  // Marshall some integral types.
  mint = -156;
  mlong = UINT32_MAX;
  CHECK_EQ(mgmt_message_marshall(msgbuf, 1, ifields, countof(ifields), &mint, &mlong), -1);
  CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), ifields, countof(ifields), &mint, &mlong), 12);
  CHECK_EQ(mgmt_message_parse(msgbuf, 1, ifields, countof(ifields), &mint, &mlong), -1);
  CHECK_EQ(mgmt_message_parse(msgbuf, sizeof(msgbuf), ifields, countof(ifields), &mint, &mlong), 12);
  CHECK_VALUE(mint, -156, "%" PRId32);
  CHECK_VALUE(mlong, static_cast<MgmtMarshallLong>(UINT32_MAX), "%" PRId64);

  // Marshall a string.
  for (unsigned i = 0; i < countof(stringvals); ++i) {
    const char * s = stringvals[i];
    size_t len = 4 /* length */ + (s ? strlen(s) : 0) /* bytes */ + 1 /* NULL */;

    mstring = s ? ats_strdup(s) : NULL;
    CHECK_EQ(mgmt_message_marshall(msgbuf, 1, sfields, countof(sfields), &mstring), -1);
    CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), sfields, countof(sfields), &mstring), len);
    ats_free(mstring); mstring = NULL;

    CHECK_EQ(mgmt_message_parse(msgbuf, 1, sfields, countof(sfields), &mstring), -1);
    CHECK_EQ(mgmt_message_parse(msgbuf, sizeof(msgbuf), sfields, countof(sfields), &mstring), len);
    CHECK_STRING(s, mstring);
    ats_free(mstring); mstring = NULL;
  }

  // Marshall data.
  mdata.ptr = ats_strdup(alpha);
  mdata.len = strlen(alpha);
  CHECK_EQ(mgmt_message_marshall(msgbuf, 10, dfields, countof(dfields), &mdata), -1);
  CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  ats_free(mdata.ptr); ink_zero(mdata);

  CHECK_EQ(mgmt_message_parse(msgbuf, strlen(alpha), dfields, countof(dfields), &mdata), -1);
  CHECK_EQ(mgmt_message_parse(msgbuf, strlen(alpha) + 4, dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  CHECK_VALUE(mdata.len, strlen(alpha), "%zu");
  box.check(memcmp(mdata.ptr, alpha, strlen(alpha)) == 0, "unexpected mdata contents");
  ats_free(mdata.ptr); ink_zero(mdata);

  // Marshall empty data.
  CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), dfields, countof(dfields), &mdata), 4);

  mdata.ptr = (void *)99;
  mdata.len = 1000;
  CHECK_EQ(mgmt_message_parse(msgbuf, sizeof(msgbuf), dfields, countof(dfields), &mdata), 4);
  CHECK_VALUE(mdata.ptr, (void *)NULL, "%p");
  CHECK_VALUE(mdata.len, (size_t)0, "%zu");
}

REGRESSION_TEST(MessageLength)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus, REGRESSION_TEST_PASSED);

  MgmtMarshallInt     mint = 0;
  MgmtMarshallLong    mlong = 0;
  MgmtMarshallString  mstring = NULL;
  MgmtMarshallData    mdata = { NULL, 0 };

  // Check invalid marshall type.
  CHECK_EQ(mgmt_message_length(inval, countof(inval), NULL), -1);

  // Check empty types array.
  CHECK_EQ(mgmt_message_length(NULL, 0), 0);

  CHECK_EQ(mgmt_message_length(ifields, countof(ifields), &mint, &mlong), 12);

  // string messages include a 4-byte length and the NULL
  mstring = (char *)"foo";
  CHECK_EQ(mgmt_message_length(sfields, countof(sfields), &mstring), sizeof("foo") + 4);

  // NULL strings are the same as empty strings ...
  mstring = NULL;
  CHECK_EQ(mgmt_message_length(sfields, countof(sfields), &mstring), 4 + 1);
  mstring = (char *)"";
  CHECK_EQ(mgmt_message_length(sfields, countof(sfields), &mstring), 4 + 1);

  // data fields include a 4-byte length. We don't go looking at the data in this case.
  mdata.len = 99;
  mdata.ptr = NULL;
  CHECK_EQ(mgmt_message_length(dfields, countof(dfields), &mdata), 99 + 4);

  mstring = (char *)"all fields";
  mdata.len = 31;
  CHECK_EQ(mgmt_message_length(afields, countof(afields), &mdata, &mint, &mlong, &mstring, &mlong, &mlong),
      31 + 4 + 4 + 8 + sizeof("all fields") + 4 + 8 + 8);

  mdata.ptr = NULL;
  mdata.len = 0;
  CHECK_EQ(mgmt_message_length(dfields, countof(dfields), &mdata), 4);
}

int main(void)
{
  RegressionTest::run();
  return RegressionTest::final_status == REGRESSION_TEST_PASSED ? 0 : 1;
}
<commit_msg>test_marshall: pass the right sockaddr length to bind(2)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 "ink_defs.h"
#include "ink_thread.h"
#include "ink_inet.h"

#include <ts/TestBox.h>
#include <MgmtMarshall.h>
#include <MgmtSocket.h>

#define CHECK_EQ(expr, len) do { \
  MgmtMarshallInt rcvd = static_cast<MgmtMarshallInt>(expr); \
  box.check(rcvd == static_cast<MgmtMarshallInt>(len), "%s returned length %d, expected %d", #expr, rcvd, static_cast<MgmtMarshallInt>(len)); \
} while (0)

#define CHECK_VALUE(value, expect, fmt) do { \
  box.check((value) == (expect), "received marshalled value " fmt ", expected " fmt "", (value), (expect)); \
} while (0)

// The NULL string is marshalled the same as the empty string.
#define CHECK_STRING(value, expect) do { \
  if (value) { \
    box.check(strcmp((value), (expect)) == 0, "received marshalled value '%s', expected '%s'", (value), (expect)); \
  } else { \
    box.check(strcmp((expect), "") == 0, "received marshalled value '%s', expected ''", (expect)); \
  } \
} while (0)

const MgmtMarshallType inval[] = {
  (MgmtMarshallType)1568
};

const MgmtMarshallType ifields[] = {
  MGMT_MARSHALL_INT, MGMT_MARSHALL_LONG
};

const MgmtMarshallType sfields[] = {
  MGMT_MARSHALL_STRING,
};

const MgmtMarshallType dfields[] = {
  MGMT_MARSHALL_DATA,
};

const MgmtMarshallType afields[] = {
  MGMT_MARSHALL_DATA,
  MGMT_MARSHALL_INT,
  MGMT_MARSHALL_LONG,
  MGMT_MARSHALL_STRING,
  MGMT_MARSHALL_LONG,
  MGMT_MARSHALL_LONG,
};

const char alpha[] = "abcdefghijklmnopqrstuvwxyz0123456789";
const char * stringvals[] = {
  NULL,
  "",
  "randomstring"
};

static bool
errno_is_continue()
{
  return errno == EALREADY || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EAGAIN || mgmt_transient_error();
}

static int
message_connect_channel(RegressionTest * t, int listenfd, int clientfd, int serverport)
{
//  bool need_connect = true;
  bool need_accept = true;
  int serverfd = -1;

  struct sockaddr_in in;

  ink_zero(in);
  in.sin_family = AF_INET;
  in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  in.sin_port = htons(serverport);

  fcntl(clientfd, F_SETFL, O_NONBLOCK);
  fcntl(listenfd, F_SETFL, O_NONBLOCK);

  connect(clientfd, (const struct sockaddr *)&in, sizeof(in));

    while (need_accept) {
      serverfd = accept(listenfd, NULL, NULL);
      if (serverfd == -1) {
      rprintf(t, "accepting, %d %s\n", errno, strerror(errno));
        if (!errno_is_continue()) {
          return -1;
        }
        ink_thr_yield();
      } else {
        need_accept = false;
      }
    }

  return serverfd;
}

static int
message_listen(int& port)
{
  IpEndpoint sa;
  socklen_t slen;
  int fd;

  fd = mgmt_socket(AF_INET, SOCK_STREAM, 0);
  if (fd == -1) {
    goto fail;
  }

  sa.setToAnyAddr(AF_INET);
  if (bind(fd, &sa.sa, sizeof(sa.sin)) == -1) {
    goto fail;
  }

  slen = sizeof(sa);
  if (getsockname(fd, &sa.sa, &slen) == -1) {
    goto fail;
  }

  port = ntohs(ats_ip_port_cast(&sa.sa));

  listen(fd, 5);
  return fd;

fail:
  close(fd);
  return -1;
}

REGRESSION_TEST(MessageReadWriteA)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus, REGRESSION_TEST_PASSED);

  int listenfd = -1;
  int serverfd = -1;
  int clientfd = -1;
  int serverport = 0;

  MgmtMarshallInt     mint = 0;
  MgmtMarshallLong    mlong = 0;
  MgmtMarshallString  mstring = NULL;
  MgmtMarshallData    mdata = { NULL, 0 };

  clientfd = mgmt_socket(AF_INET, SOCK_STREAM, 0);
  listenfd = message_listen(serverport);
  serverfd = message_connect_channel(t, listenfd, clientfd, serverport);
  rprintf(t, "listenfd=%d clientfd=%d, serverfd=%d, port=%d\n", listenfd, clientfd, serverfd, serverport);

  fcntl(clientfd, F_SETFL, O_NDELAY);
  fcntl(serverfd, F_SETFL, O_NDELAY);

  mint = 99;
  mlong = (MgmtMarshallLong)(&listenfd);

  // Check invalid Fd write
  CHECK_EQ(mgmt_message_write(5000, ifields, countof(ifields), &mint, &mlong), -1);

  CHECK_EQ(mgmt_message_write(clientfd, ifields, countof(ifields), &mint, &mlong), 12);

  mint = 0;
  mlong = 0;
  CHECK_EQ(mgmt_message_read(serverfd, ifields, countof(ifields), &mint, &mlong), 12);
  CHECK_VALUE(mint, 99, "%" PRId32);
  CHECK_VALUE(mlong, (MgmtMarshallLong)(&listenfd), "%" PRId64);

  // Marshall a string.
  for (unsigned i = 0; i < countof(stringvals); ++i) {
    const char * s = stringvals[i];
    size_t len = 4 /* length */ + (s ? strlen(s) : 0) /* bytes */ + 1 /* NULL */;

    mstring = s ? ats_strdup(s) : NULL;
    CHECK_EQ(mgmt_message_write(clientfd, sfields, countof(sfields), &mstring), len);
    ats_free(mstring); mstring = NULL;

    CHECK_EQ(mgmt_message_read(serverfd, sfields, countof(sfields), &mstring), len);
    CHECK_STRING(s, mstring);
    ats_free(mstring); mstring = NULL;
  }

  // Marshall data.
  mdata.ptr = ats_strdup(alpha);
  mdata.len = strlen(alpha);
  CHECK_EQ(mgmt_message_write(clientfd, dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  ats_free(mdata.ptr); ink_zero(mdata);

  CHECK_EQ(mgmt_message_read(serverfd, dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  CHECK_VALUE(mdata.len, strlen(alpha), "%zu");
  box.check(memcmp(mdata.ptr, alpha, strlen(alpha)) == 0, "unexpected mdata contents");
  ats_free(mdata.ptr); ink_zero(mdata);

  close(clientfd);
  close(listenfd);
  close(serverfd);
}

REGRESSION_TEST(MessageMarshall)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus, REGRESSION_TEST_PASSED);

  char msgbuf[4096];

  MgmtMarshallInt     mint = 0;
  MgmtMarshallLong    mlong = 0;
  MgmtMarshallString  mstring = NULL;
  MgmtMarshallData    mdata = { NULL, 0 };

  // Parse empty message.
  CHECK_EQ(mgmt_message_parse(NULL, 0, NULL, 0), 0);

  // Marshall empty message.
  CHECK_EQ(mgmt_message_marshall(NULL, 0, NULL, 0), 0);

  // Marshall some integral types.
  mint = -156;
  mlong = UINT32_MAX;
  CHECK_EQ(mgmt_message_marshall(msgbuf, 1, ifields, countof(ifields), &mint, &mlong), -1);
  CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), ifields, countof(ifields), &mint, &mlong), 12);
  CHECK_EQ(mgmt_message_parse(msgbuf, 1, ifields, countof(ifields), &mint, &mlong), -1);
  CHECK_EQ(mgmt_message_parse(msgbuf, sizeof(msgbuf), ifields, countof(ifields), &mint, &mlong), 12);
  CHECK_VALUE(mint, -156, "%" PRId32);
  CHECK_VALUE(mlong, static_cast<MgmtMarshallLong>(UINT32_MAX), "%" PRId64);

  // Marshall a string.
  for (unsigned i = 0; i < countof(stringvals); ++i) {
    const char * s = stringvals[i];
    size_t len = 4 /* length */ + (s ? strlen(s) : 0) /* bytes */ + 1 /* NULL */;

    mstring = s ? ats_strdup(s) : NULL;
    CHECK_EQ(mgmt_message_marshall(msgbuf, 1, sfields, countof(sfields), &mstring), -1);
    CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), sfields, countof(sfields), &mstring), len);
    ats_free(mstring); mstring = NULL;

    CHECK_EQ(mgmt_message_parse(msgbuf, 1, sfields, countof(sfields), &mstring), -1);
    CHECK_EQ(mgmt_message_parse(msgbuf, sizeof(msgbuf), sfields, countof(sfields), &mstring), len);
    CHECK_STRING(s, mstring);
    ats_free(mstring); mstring = NULL;
  }

  // Marshall data.
  mdata.ptr = ats_strdup(alpha);
  mdata.len = strlen(alpha);
  CHECK_EQ(mgmt_message_marshall(msgbuf, 10, dfields, countof(dfields), &mdata), -1);
  CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  ats_free(mdata.ptr); ink_zero(mdata);

  CHECK_EQ(mgmt_message_parse(msgbuf, strlen(alpha), dfields, countof(dfields), &mdata), -1);
  CHECK_EQ(mgmt_message_parse(msgbuf, strlen(alpha) + 4, dfields, countof(dfields), &mdata), 4 + strlen(alpha));
  CHECK_VALUE(mdata.len, strlen(alpha), "%zu");
  box.check(memcmp(mdata.ptr, alpha, strlen(alpha)) == 0, "unexpected mdata contents");
  ats_free(mdata.ptr); ink_zero(mdata);

  // Marshall empty data.
  CHECK_EQ(mgmt_message_marshall(msgbuf, sizeof(msgbuf), dfields, countof(dfields), &mdata), 4);

  mdata.ptr = (void *)99;
  mdata.len = 1000;
  CHECK_EQ(mgmt_message_parse(msgbuf, sizeof(msgbuf), dfields, countof(dfields), &mdata), 4);
  CHECK_VALUE(mdata.ptr, (void *)NULL, "%p");
  CHECK_VALUE(mdata.len, (size_t)0, "%zu");
}

REGRESSION_TEST(MessageLength)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus, REGRESSION_TEST_PASSED);

  MgmtMarshallInt     mint = 0;
  MgmtMarshallLong    mlong = 0;
  MgmtMarshallString  mstring = NULL;
  MgmtMarshallData    mdata = { NULL, 0 };

  // Check invalid marshall type.
  CHECK_EQ(mgmt_message_length(inval, countof(inval), NULL), -1);

  // Check empty types array.
  CHECK_EQ(mgmt_message_length(NULL, 0), 0);

  CHECK_EQ(mgmt_message_length(ifields, countof(ifields), &mint, &mlong), 12);

  // string messages include a 4-byte length and the NULL
  mstring = (char *)"foo";
  CHECK_EQ(mgmt_message_length(sfields, countof(sfields), &mstring), sizeof("foo") + 4);

  // NULL strings are the same as empty strings ...
  mstring = NULL;
  CHECK_EQ(mgmt_message_length(sfields, countof(sfields), &mstring), 4 + 1);
  mstring = (char *)"";
  CHECK_EQ(mgmt_message_length(sfields, countof(sfields), &mstring), 4 + 1);

  // data fields include a 4-byte length. We don't go looking at the data in this case.
  mdata.len = 99;
  mdata.ptr = NULL;
  CHECK_EQ(mgmt_message_length(dfields, countof(dfields), &mdata), 99 + 4);

  mstring = (char *)"all fields";
  mdata.len = 31;
  CHECK_EQ(mgmt_message_length(afields, countof(afields), &mdata, &mint, &mlong, &mstring, &mlong, &mlong),
      31 + 4 + 4 + 8 + sizeof("all fields") + 4 + 8 + 8);

  mdata.ptr = NULL;
  mdata.len = 0;
  CHECK_EQ(mgmt_message_length(dfields, countof(dfields), &mdata), 4);
}

int main(void)
{
  RegressionTest::run();
  return RegressionTest::final_status == REGRESSION_TEST_PASSED ? 0 : 1;
}
<|endoftext|>


<commit_before>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// library configuration
#include "libmesh_config.h"

// C++ includes
#include <algorithm> // for std::min
#include <map>       // for std::multimap
#include <sstream>   // for std::ostringstream

// Local includes
#include "boundary_info.h"
#include "elem.h"
#include "mesh_base.h"
#include "parallel.h"
#include "partitioner.h"
#include "point_locator_base.h"

namespace libMesh
{

// ------------------------------------------------------------
// MeshBase class member functions
MeshBase::MeshBase (unsigned int d) :
  boundary_info  (new BoundaryInfo(*this)),
  _n_parts       (1),
  _dim           (d),
  _is_prepared   (false),
  _point_locator (NULL),
  _partitioner   (NULL)
{
  libmesh_assert (LIBMESH_DIM <= 3);
  libmesh_assert (LIBMESH_DIM >= _dim);
  libmesh_assert (libMesh::initialized());
}

MeshBase::MeshBase (const MeshBase& other_mesh) :
  boundary_info  (new BoundaryInfo(*this)), // no copy constructor defined for BoundaryInfo?
  _n_parts       (other_mesh._n_parts),
  _dim           (other_mesh._dim),
  _is_prepared   (other_mesh._is_prepared),
  _point_locator (NULL),
  _partitioner   (other_mesh._partitioner->clone())
{
}

MeshBase::~MeshBase()
{
  this->clear();

  libmesh_assert (!libMesh::closed());
}

void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements)
{
  // Renumber the nodes and elements so that they in contiguous
  // blocks.  By default, skip_renumber_nodes_and_elements is false,
  // however we may skip this step by passing
  // skip_renumber_nodes_and_elements==true to this function.
  //
  // Instances where you if prepare_for_use() should not renumber the nodes
  // and elements include reading in e.g. an xda/r or gmv file. In
  // this case, the ordering of the nodes may depend on an accompanying
  // solution, and the node ordering cannot be changed.
  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Let all the elements find their neighbors
  this->find_neighbors();

  // Partition the mesh.
  this->partition();

  // If we're using ParallelMesh, we'll want it parallelized.
  this->delete_remote_elements();

  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Reset our PointLocator.  This needs to happen any time the elements
  // in the underlying elements in the mesh have changed, so we do it here.
  this->clear_point_locator();

  // The mesh is now prepared for use.
  _is_prepared = true;
}

void MeshBase::clear ()
{
  // Reset the number of partitions
  _n_parts = 1;

  // Reset the _is_prepared flag
  _is_prepared = false;

  // Clear boundary information
  this->boundary_info->clear();

  // Clear our point locator.
  this->clear_point_locator();
}

unsigned int MeshBase::n_subdomains() const
{
  // This requires an inspection on every processor
  parallel_only();

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  std::set<unsigned int> subdomain_ids;

  for (; el!=end; ++el)
    subdomain_ids.insert((*el)->subdomain_id());

  // Some subdomains may only live on other processors
  Parallel::set_union(subdomain_ids, 0);

  unsigned int n_sbd_ids = subdomain_ids.size();
  Parallel::broadcast(n_sbd_ids);

  return n_sbd_ids;
}

unsigned int MeshBase::n_nodes_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's nodes or unpartitioned
  // nodes
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_nodes_begin(proc_id),
						  this->pid_nodes_end  (proc_id)));
}

unsigned int MeshBase::n_elem_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's elements or unpartitioned
  // elements
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_elements_begin(proc_id),
						  this->pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_active_elem_on_proc (const unsigned int proc_id) const
{
  libmesh_assert (proc_id < libMesh::n_processors());
  return static_cast<unsigned int>(std::distance (this->active_pid_elements_begin(proc_id),
						  this->active_pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->elements_begin();
  const const_element_iterator end = this->elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

unsigned int MeshBase::n_active_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

std::string MeshBase::get_info() const
{
  std::ostringstream out;

  out << " Mesh Information:"                                  << '\n'
      << "  mesh_dimension()="    << this->mesh_dimension()    << '\n'
      << "  spatial_dimension()=" << this->spatial_dimension() << '\n'
      << "  n_nodes()="           << this->n_nodes()           << '\n'
      << "    n_local_nodes()="   << this->n_local_nodes()     << '\n'
      << "  n_elem()="            << this->n_elem()            << '\n'
      << "    n_local_elem()="    << this->n_local_elem()      << '\n'
#ifdef LIBMESH_ENABLE_AMR
      << "    n_active_elem()="   << this->n_active_elem()     << '\n'
#endif
      << "  n_subdomains()="      << this->n_subdomains()      << '\n'
      << "  n_processors()="      << this->n_processors()      << '\n'
      << "  processor_id()="      << this->processor_id()      << '\n';

  return out.str();
}

void MeshBase::print_info(std::ostream& os) const
{
  os << this->get_info()
     << std::endl;
}

std::ostream& operator << (std::ostream& os, const MeshBase& m)
{
  m.print_info(os);
  return os;
}

void MeshBase::partition (const unsigned int n_parts)
{
  if (partitioner().get()) // "NULL" means don't partition
    partitioner()->partition (*this, n_parts);
}

unsigned int MeshBase::recalculate_n_partitions()
{
  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  unsigned int max_proc_id=0;

  for (; el!=end; ++el)
    max_proc_id = std::max(max_proc_id, static_cast<unsigned int>((*el)->processor_id()));

  // The number of partitions is one more than the max processor ID.
  _n_parts = max_proc_id+1;

  return _n_parts;
}

const PointLocatorBase & MeshBase::point_locator () const
{
  // Double checked lock pattern for efficiency
  if (_point_locator.get() == NULL)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);

    if (_point_locator.get() == NULL)
      _point_locator.reset (PointLocatorBase::build(TREE, *this).release());
  }

  return *_point_locator;
}

void MeshBase::clear_point_locator ()
{
  _point_locator.reset(NULL);
}

} // namespace libMesh

<commit_msg>Remove unnecessary/buggy mutex acquisition from point_locator()<commit_after>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// library configuration
#include "libmesh_config.h"

// C++ includes
#include <algorithm> // for std::min
#include <map>       // for std::multimap
#include <sstream>   // for std::ostringstream

// Local includes
#include "boundary_info.h"
#include "elem.h"
#include "mesh_base.h"
#include "parallel.h"
#include "partitioner.h"
#include "point_locator_base.h"
#include "threads.h"

namespace libMesh
{

// ------------------------------------------------------------
// MeshBase class member functions
MeshBase::MeshBase (unsigned int d) :
  boundary_info  (new BoundaryInfo(*this)),
  _n_parts       (1),
  _dim           (d),
  _is_prepared   (false),
  _point_locator (NULL),
  _partitioner   (NULL)
{
  libmesh_assert (LIBMESH_DIM <= 3);
  libmesh_assert (LIBMESH_DIM >= _dim);
  libmesh_assert (libMesh::initialized());
}

MeshBase::MeshBase (const MeshBase& other_mesh) :
  boundary_info  (new BoundaryInfo(*this)), // no copy constructor defined for BoundaryInfo?
  _n_parts       (other_mesh._n_parts),
  _dim           (other_mesh._dim),
  _is_prepared   (other_mesh._is_prepared),
  _point_locator (NULL),
  _partitioner   (other_mesh._partitioner->clone())
{
}

MeshBase::~MeshBase()
{
  this->clear();

  libmesh_assert (!libMesh::closed());
}

void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements)
{
  // Renumber the nodes and elements so that they in contiguous
  // blocks.  By default, skip_renumber_nodes_and_elements is false,
  // however we may skip this step by passing
  // skip_renumber_nodes_and_elements==true to this function.
  //
  // Instances where you if prepare_for_use() should not renumber the nodes
  // and elements include reading in e.g. an xda/r or gmv file. In
  // this case, the ordering of the nodes may depend on an accompanying
  // solution, and the node ordering cannot be changed.
  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Let all the elements find their neighbors
  this->find_neighbors();

  // Partition the mesh.
  this->partition();

  // If we're using ParallelMesh, we'll want it parallelized.
  this->delete_remote_elements();

  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Reset our PointLocator.  This needs to happen any time the elements
  // in the underlying elements in the mesh have changed, so we do it here.
  this->clear_point_locator();

  // The mesh is now prepared for use.
  _is_prepared = true;
}

void MeshBase::clear ()
{
  // Reset the number of partitions
  _n_parts = 1;

  // Reset the _is_prepared flag
  _is_prepared = false;

  // Clear boundary information
  this->boundary_info->clear();

  // Clear our point locator.
  this->clear_point_locator();
}

unsigned int MeshBase::n_subdomains() const
{
  // This requires an inspection on every processor
  parallel_only();

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  std::set<unsigned int> subdomain_ids;

  for (; el!=end; ++el)
    subdomain_ids.insert((*el)->subdomain_id());

  // Some subdomains may only live on other processors
  Parallel::set_union(subdomain_ids, 0);

  unsigned int n_sbd_ids = subdomain_ids.size();
  Parallel::broadcast(n_sbd_ids);

  return n_sbd_ids;
}

unsigned int MeshBase::n_nodes_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's nodes or unpartitioned
  // nodes
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_nodes_begin(proc_id),
						  this->pid_nodes_end  (proc_id)));
}

unsigned int MeshBase::n_elem_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's elements or unpartitioned
  // elements
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_elements_begin(proc_id),
						  this->pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_active_elem_on_proc (const unsigned int proc_id) const
{
  libmesh_assert (proc_id < libMesh::n_processors());
  return static_cast<unsigned int>(std::distance (this->active_pid_elements_begin(proc_id),
						  this->active_pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->elements_begin();
  const const_element_iterator end = this->elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

unsigned int MeshBase::n_active_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

std::string MeshBase::get_info() const
{
  std::ostringstream out;

  out << " Mesh Information:"                                  << '\n'
      << "  mesh_dimension()="    << this->mesh_dimension()    << '\n'
      << "  spatial_dimension()=" << this->spatial_dimension() << '\n'
      << "  n_nodes()="           << this->n_nodes()           << '\n'
      << "    n_local_nodes()="   << this->n_local_nodes()     << '\n'
      << "  n_elem()="            << this->n_elem()            << '\n'
      << "    n_local_elem()="    << this->n_local_elem()      << '\n'
#ifdef LIBMESH_ENABLE_AMR
      << "    n_active_elem()="   << this->n_active_elem()     << '\n'
#endif
      << "  n_subdomains()="      << this->n_subdomains()      << '\n'
      << "  n_processors()="      << this->n_processors()      << '\n'
      << "  processor_id()="      << this->processor_id()      << '\n';

  return out.str();
}

void MeshBase::print_info(std::ostream& os) const
{
  os << this->get_info()
     << std::endl;
}

std::ostream& operator << (std::ostream& os, const MeshBase& m)
{
  m.print_info(os);
  return os;
}

void MeshBase::partition (const unsigned int n_parts)
{
  if (partitioner().get()) // "NULL" means don't partition
    partitioner()->partition (*this, n_parts);
}

unsigned int MeshBase::recalculate_n_partitions()
{
  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  unsigned int max_proc_id=0;

  for (; el!=end; ++el)
    max_proc_id = std::max(max_proc_id, static_cast<unsigned int>((*el)->processor_id()));

  // The number of partitions is one more than the max processor ID.
  _n_parts = max_proc_id+1;

  return _n_parts;
}

const PointLocatorBase & MeshBase::point_locator () const
{
  if (_point_locator.get() == NULL)
    {
      // PointLocator construction may not be safe within threads
      libmesh_assert(!Threads::in_threads);

      _point_locator.reset (PointLocatorBase::build(TREE, *this).release());
    }
}

void MeshBase::clear_point_locator ()
{
  _point_locator.reset(NULL);
}

} // namespace libMesh

<|endoftext|>


<commit_before>#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <sstream>
#include <iostream>
#include <stdexcept>

#include <GLFW/glfw3.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

static int xioctl(int fh, int request, void *arg)
{
    int r;
    do {
        r = ioctl(fh, request, arg);
    } while (r < 0 && errno == EINTR);
    return r;
}

struct buffer { void * start; size_t length; };
#pragma pack(push, 1)
struct z16y8_pixel { uint16_t z; uint8_t y; };
#pragma pack(pop)

void throw_error(const char * s)
{
    std::ostringstream ss;
    ss << s << " error " << errno << ", " << strerror(errno);
    throw std::runtime_error(s);
}

void warn_error(const char * s)
{
    std::cerr << s << " error " << errno << ", " << strerror(errno) << std::endl;
}

class subdevice
{
    std::string dev_name;
    int fd;
    std::vector<buffer> buffers;
public:
    subdevice(const std::string & dev_name) : dev_name(dev_name), fd()
    {
        struct stat st;
        if(stat(dev_name.c_str(), &st) < 0)
        {
            std::ostringstream ss; ss << "Cannot identify '" << dev_name << "': " << errno << ", " << strerror(errno);
            throw std::runtime_error(ss.str());
        }
        if(!S_ISCHR(st.st_mode)) throw std::runtime_error(dev_name + " is no device");

        fd = open(dev_name.c_str(), O_RDWR | O_NONBLOCK, 0);
        if(fd < 0)
        {
            std::ostringstream ss; ss << "Cannot open '" << dev_name << "': " << errno << ", " << strerror(errno);
            throw std::runtime_error(ss.str());
        }

        v4l2_capability cap = {};
        if(xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
        {
            if(errno == EINVAL) throw std::runtime_error(dev_name + " is no V4L2 device");
            else throw_error("VIDIOC_QUERYCAP");
        }
        if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) throw std::runtime_error(dev_name + " is no video capture device");
        if (!(cap.capabilities & V4L2_CAP_STREAMING)) throw std::runtime_error(dev_name + " does not support streaming I/O");

        // Select video input, video standard and tune here.
        v4l2_cropcap cropcap = {};
        cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap))
        {
            v4l2_crop crop = {};
            crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            crop.c = cropcap.defrect; // reset to default
            if (xioctl(fd, VIDIOC_S_CROP, &crop) < 0)
            {
                switch (errno)
                {
                case EINVAL: break; // Cropping not supported
                default: break; // Errors ignored
                }
            }
        } else {} // Errors ignored
    }

    void set_format(int width, int height, int pixelformat)
    {
        v4l2_format fmt = {};
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width       = width;
        fmt.fmt.pix.height      = height;
        fmt.fmt.pix.pixelformat = pixelformat;
        fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
        if(xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) throw_error("VIDIOC_S_FMT");
        // Note VIDIOC_S_FMT may change width and height
    }

    void start_capture()
    {
        v4l2_format fmt = {};
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if(xioctl(fd, VIDIOC_G_FMT, &fmt) < 0) throw_error("VIDIOC_G_FMT");

        // Buggy driver paranoia.
        unsigned int min = fmt.fmt.pix.width * 2;
        if(fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
        min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
        if(fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;

        // Init memory mapped IO
        v4l2_requestbuffers req = {};
        req.count = 4;
        req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory = V4L2_MEMORY_MMAP;
        if(xioctl(fd, VIDIOC_REQBUFS, &req) < 0)
        {
            if(errno == EINVAL) throw std::runtime_error(dev_name + " does not support memory mapping");
            else throw_error("VIDIOC_REQBUFS");
        }
        if(req.count < 2)
        {
            throw std::runtime_error("Insufficient buffer memory on " + dev_name);
        }

        buffers.resize(req.count);
        for(int i=0; i<buffers.size(); ++i)
        {
            v4l2_buffer buf = {};
            buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory      = V4L2_MEMORY_MMAP;
            buf.index       = i;
            if(xioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) throw_error("VIDIOC_QUERYBUF");

            buffers[i].length = buf.length;
            buffers[i].start = mmap(NULL /* start anywhere */,
                          buf.length,
                          PROT_READ | PROT_WRITE /* required */,
                          MAP_SHARED /* recommended */,
                          fd, buf.m.offset);
            if(buffers[i].start == MAP_FAILED) throw_error("mmap");
        }

        // Start capturing
        for(int i = 0; i < buffers.size(); ++i)
        {
            v4l2_buffer buf = {};
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            buf.index = i;
            if(xioctl(fd, VIDIOC_QBUF, &buf) < 0) throw_error("VIDIOC_QBUF");
        }

        v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if(xioctl(fd, VIDIOC_STREAMON, &type) < 0) throw_error("VIDIOC_STREAMON");
    }

    ~subdevice()
    {
        v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if(xioctl(fd, VIDIOC_STREAMOFF, &type) < 0) warn_error("VIDIOC_STREAMOFF");

        for(int i = 0; i < buffers.size(); ++i)
        {
            if(munmap(buffers[i].start, buffers[i].length) < 0) warn_error("munmap");
        }

        if(close(fd) < 0) warn_error("close");
    }

    template<class F> void poll(F f)
    {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(fd, &fds);

        struct timeval tv;
        tv.tv_sec = 2;
        tv.tv_usec = 0;

        int r = select(fd + 1, &fds, NULL, NULL, &tv);
        if(r < 0)
        {
            if (errno == EINTR) return;
            throw_error("select");
        }

        if(r == 1)
        {
            v4l2_buffer buf = {};
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            if(xioctl(fd, VIDIOC_DQBUF, &buf) < 0)
            {
                if(errno == EAGAIN) return;
                throw_error("VIDIOC_DQBUF");
            }
            assert(buf.index < buffers.size());

            f(buffers[buf.index].start, buf.bytesused);

            if(xioctl(fd, VIDIOC_QBUF, &buf) < 0) throw_error("VIDIOC_QBUF");
        }
    }
};

int main(int argc, char * argv[])
{
    subdevice dev0("/dev/video0"), dev1("/dev/video1");
    dev0.set_format(640, 480, V4L2_PIX_FMT_YUYV);
    dev0.start_capture();
    dev1.set_format(640, 480, v4l2_fourcc('I','N','V','R'));
    dev1.start_capture();

    // Open a GLFW window
    glfwInit();
    GLFWwindow * win = glfwCreateWindow(1280, 480, "V4L2 test", 0, 0);
    glfwMakeContextCurrent(win);

    // While window is open
    uint16_t z[640*480] = {};
    uint8_t yuy2[640*480*2] = {};
    while (!glfwWindowShouldClose(win))
    {
        glfwPollEvents();

        dev0.poll([&](const void * data, size_t size) { if(size == sizeof(yuy2)) memcpy(yuy2, data, size); });
        dev1.poll([&](const void * data, size_t size) { if(size == sizeof(z)) memcpy(z, data, size); });

        int w,h;
        glfwGetFramebufferSize(win, &w, &h);
        glViewport(0, 0, w, h);
        glClear(GL_COLOR_BUFFER_BIT);

        glPushMatrix();
        glfwGetWindowSize(win, &w, &h);
        glOrtho(0, w, h, 0, -1, +1);

        glRasterPos2i(0, 480);
        glDrawPixels(640, 480, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, yuy2);
        glRasterPos2i(640, 480);
        glDrawPixels(640, 480, GL_LUMINANCE, GL_UNSIGNED_SHORT, z);

        glPopMatrix();
        glfwSwapBuffers(win);
    }
    glfwDestroyWindow(win);
    glfwTerminate();

    return EXIT_SUCCESS;
}

<commit_msg>Check multiple subdevices in a single select statement<commit_after>#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <cstring>

#include <vector>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <functional>

#include <GLFW/glfw3.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

static int xioctl(int fh, int request, void *arg)
{
    int r;
    do {
        r = ioctl(fh, request, arg);
    } while (r < 0 && errno == EINTR);
    return r;
}

struct buffer { void * start; size_t length; };
#pragma pack(push, 1)
struct z16y8_pixel { uint16_t z; uint8_t y; };
#pragma pack(pop)

void throw_error(const char * s)
{
    std::ostringstream ss;
    ss << s << " error " << errno << ", " << strerror(errno);
    throw std::runtime_error(s);
}

void warn_error(const char * s)
{
    std::cerr << s << " error " << errno << ", " << strerror(errno) << std::endl;
}

class subdevice
{
    std::string dev_name;
    int fd;
    std::vector<buffer> buffers;
    std::function<void(const void *, size_t)> callback;
public:
    subdevice(const std::string & dev_name) : dev_name(dev_name), fd()
    {
        struct stat st;
        if(stat(dev_name.c_str(), &st) < 0)
        {
            std::ostringstream ss; ss << "Cannot identify '" << dev_name << "': " << errno << ", " << strerror(errno);
            throw std::runtime_error(ss.str());
        }
        if(!S_ISCHR(st.st_mode)) throw std::runtime_error(dev_name + " is no device");

        fd = open(dev_name.c_str(), O_RDWR | O_NONBLOCK, 0);
        if(fd < 0)
        {
            std::ostringstream ss; ss << "Cannot open '" << dev_name << "': " << errno << ", " << strerror(errno);
            throw std::runtime_error(ss.str());
        }

        v4l2_capability cap = {};
        if(xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
        {
            if(errno == EINVAL) throw std::runtime_error(dev_name + " is no V4L2 device");
            else throw_error("VIDIOC_QUERYCAP");
        }
        if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) throw std::runtime_error(dev_name + " is no video capture device");
        if (!(cap.capabilities & V4L2_CAP_STREAMING)) throw std::runtime_error(dev_name + " does not support streaming I/O");

        // Select video input, video standard and tune here.
        v4l2_cropcap cropcap = {};
        cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap))
        {
            v4l2_crop crop = {};
            crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            crop.c = cropcap.defrect; // reset to default
            if (xioctl(fd, VIDIOC_S_CROP, &crop) < 0)
            {
                switch (errno)
                {
                case EINVAL: break; // Cropping not supported
                default: break; // Errors ignored
                }
            }
        } else {} // Errors ignored
    }

    ~subdevice()
    {
        v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if(xioctl(fd, VIDIOC_STREAMOFF, &type) < 0) warn_error("VIDIOC_STREAMOFF");

        for(int i = 0; i < buffers.size(); ++i)
        {
            if(munmap(buffers[i].start, buffers[i].length) < 0) warn_error("munmap");
        }

        if(close(fd) < 0) warn_error("close");
    }

    void set_format(int width, int height, int pixelformat)
    {
        v4l2_format fmt = {};
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width       = width;
        fmt.fmt.pix.height      = height;
        fmt.fmt.pix.pixelformat = pixelformat;
        fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
        if(xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) throw_error("VIDIOC_S_FMT");
        // Note VIDIOC_S_FMT may change width and height
    }

    void start_capture(std::function<void(const void * data, size_t size)> callback)
    {
        v4l2_format fmt = {};
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if(xioctl(fd, VIDIOC_G_FMT, &fmt) < 0) throw_error("VIDIOC_G_FMT");

        // Buggy driver paranoia.
        unsigned int min = fmt.fmt.pix.width * 2;
        if(fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
        min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
        if(fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;

        // Init memory mapped IO
        v4l2_requestbuffers req = {};
        req.count = 4;
        req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory = V4L2_MEMORY_MMAP;
        if(xioctl(fd, VIDIOC_REQBUFS, &req) < 0)
        {
            if(errno == EINVAL) throw std::runtime_error(dev_name + " does not support memory mapping");
            else throw_error("VIDIOC_REQBUFS");
        }
        if(req.count < 2)
        {
            throw std::runtime_error("Insufficient buffer memory on " + dev_name);
        }

        buffers.resize(req.count);
        for(int i=0; i<buffers.size(); ++i)
        {
            v4l2_buffer buf = {};
            buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory      = V4L2_MEMORY_MMAP;
            buf.index       = i;
            if(xioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) throw_error("VIDIOC_QUERYBUF");

            buffers[i].length = buf.length;
            buffers[i].start = mmap(NULL /* start anywhere */,
                          buf.length,
                          PROT_READ | PROT_WRITE /* required */,
                          MAP_SHARED /* recommended */,
                          fd, buf.m.offset);
            if(buffers[i].start == MAP_FAILED) throw_error("mmap");
        }

        // Start capturing
        for(int i = 0; i < buffers.size(); ++i)
        {
            v4l2_buffer buf = {};
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            buf.index = i;
            if(xioctl(fd, VIDIOC_QBUF, &buf) < 0) throw_error("VIDIOC_QBUF");
        }

        v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if(xioctl(fd, VIDIOC_STREAMON, &type) < 0) throw_error("VIDIOC_STREAMON");

        this->callback = callback;
    }

    static void poll(std::initializer_list<subdevice *> subdevices)
    {
        int max_fd = 0;
        fd_set fds;
        FD_ZERO(&fds);
        for(auto * sub : subdevices)
        {
            FD_SET(sub->fd, &fds);
            max_fd = std::max(max_fd, sub->fd);
        }

        struct timeval tv;
        tv.tv_sec = 2;
        tv.tv_usec = 0;

        int r = select(max_fd + 1, &fds, NULL, NULL, &tv);
        if(r < 0)
        {
            if (errno == EINTR) return;
            throw_error("select");
        }

        for(auto * sub : subdevices)
        {
            if(FD_ISSET(sub->fd, &fds))
            {
                v4l2_buffer buf = {};
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                if(xioctl(sub->fd, VIDIOC_DQBUF, &buf) < 0)
                {
                    if(errno == EAGAIN) return;
                    throw_error("VIDIOC_DQBUF");
                }
                assert(buf.index < sub->buffers.size());

                sub->callback(sub->buffers[buf.index].start, buf.bytesused);

                if(xioctl(sub->fd, VIDIOC_QBUF, &buf) < 0) throw_error("VIDIOC_QBUF");
            }
        }
    }
};

int main(int argc, char * argv[])
{
    uint16_t z[640*480] = {};
    uint8_t yuy2[640*480*2] = {};

    subdevice dev0("/dev/video0"), dev1("/dev/video1");
    dev0.set_format(640, 480, V4L2_PIX_FMT_YUYV);
    dev0.start_capture([&](const void * data, size_t size) { if(size == sizeof(yuy2)) memcpy(yuy2, data, size); });
    dev1.set_format(640, 480, v4l2_fourcc('I','N','V','R'));
    dev1.start_capture([&](const void * data, size_t size) { if(size == sizeof(z)) memcpy(z, data, size); });

    // Open a GLFW window
    glfwInit();
    GLFWwindow * win = glfwCreateWindow(1280, 480, "V4L2 test", 0, 0);
    glfwMakeContextCurrent(win);

    // While window is open
    while (!glfwWindowShouldClose(win))
    {
        glfwPollEvents();

        subdevice::poll({&dev0, &dev1});

        int w,h;
        glfwGetFramebufferSize(win, &w, &h);
        glViewport(0, 0, w, h);
        glClear(GL_COLOR_BUFFER_BIT);

        glPushMatrix();
        glfwGetWindowSize(win, &w, &h);
        glOrtho(0, w, h, 0, -1, +1);

        glRasterPos2i(0, 480);
        glDrawPixels(640, 480, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, yuy2);
        glRasterPos2i(640, 480);
        glDrawPixels(640, 480, GL_LUMINANCE, GL_UNSIGNED_SHORT, z);

        glPopMatrix();
        glfwSwapBuffers(win);
    }
    glfwDestroyWindow(win);
    glfwTerminate();

    return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>//===- FakeQuantSupport.cpp - Support utilities for FakeQuant ops ---------===//
//
// Copyright 2019 The MLIR Authors.
//
// 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 "mlir/Dialect/QuantOps/FakeQuantSupport.h"
#include "mlir/Dialect/QuantOps/QuantTypes.h"

using namespace mlir;
using namespace mlir::quant;

UniformQuantizedType
mlir::quant::fakeQuantAttrsToType(Location loc, unsigned numBits, double rmin,
                                  double rmax, bool narrowRange,
                                  Type expressedType, bool isSigned) {
  MLIRContext *ctx = expressedType.getContext();
  Type storageType;
  unsigned flags;
  int64_t qmin;
  int64_t qmax;

  // Hard-coded type mapping from TFLite.
  if (numBits <= 8) {
    storageType = IntegerType::get(8, ctx);
    if (isSigned) {
      flags = QuantizationFlags::Signed;
      qmin = -128;
      qmax = 127;
    } else {
      flags = 0;
      qmin = 0;
      qmax = 255;
    }
  } else if (numBits <= 16) {
    storageType = IntegerType::get(16, ctx);
    if (isSigned) {
      flags = QuantizationFlags::Signed;
      qmin = -32768;
      qmax = 32767;
    } else {
      flags = 0;
      qmin = 0;
      qmax = 65535;
    }
  } else {
    emitError(loc, "unsupported FakeQuant number of bits: ") << numBits;
    return nullptr;
  }

  // Handle narrowRange.
  if (narrowRange) {
    qmin += 1;
  }

  // Range must straddle zero.
  if (rmin > 0.0 || rmax < 0.0) {
    return (emitError(loc, "FakeQuant range must straddle zero: [")
                << rmin << "," << rmax << "]",
            nullptr);
  }

  // Special case where min/max is a point. Must be 0.
  if (rmin == rmax) {
    return UniformQuantizedType::getChecked(flags, storageType, expressedType,
                                            0.0, 0, qmin, qmax, loc);
  }

  // Determine the scale.
  const double qminDouble = qmin;
  const double qmaxDouble = qmax;
  const double scale = (rmax - rmin) / (qmaxDouble - qminDouble);

  // Zero point computation.
  // In float, solve the affine equation for any known pair
  // (real value, corresponding quantized value), of which, two such pairs
  // are known: (rmin, qmin), (rmax, qmax).
  // The arithmetic error on the zero point computed from either pair will be
  // roughly machine_epsilon * (sum of absolute values of terms).
  // Use the variant that adds the smaller error.
  const double zeroPointFromMin = qminDouble - rmin / scale;
  const double zeroPointFromMinError =
      std::abs(qminDouble) + std::abs(rmin / scale);
  const double zeroPointFromMax = qmaxDouble - rmax / scale;
  const double zeroPointFromMaxError =
      std::abs(qmaxDouble) + std::abs(rmax / scale);

  const double zeroPointDouble = (zeroPointFromMinError < zeroPointFromMaxError)
                                     ? zeroPointFromMin
                                     : zeroPointFromMax;

  // Now nudge the zero point to be an integer.
  int64_t nudgedZeroPoint = 0;
  if (zeroPointDouble < qminDouble) {
    nudgedZeroPoint = qmin;
  } else if (zeroPointDouble > qmaxDouble) {
    nudgedZeroPoint = qmax;
  } else {
    nudgedZeroPoint = round(zeroPointDouble);
  }

  // By construction, the nudged zero point should always be in range.
  assert(nudgedZeroPoint >= qmin);
  assert(nudgedZeroPoint <= qmax);

  return UniformQuantizedType::getChecked(flags, storageType, expressedType,
                                          scale, nudgedZeroPoint, qmin, qmax,
                                          loc);
}
<commit_msg>Fix the equality check of two floating point values<commit_after>//===- FakeQuantSupport.cpp - Support utilities for FakeQuant ops ---------===//
//
// Copyright 2019 The MLIR Authors.
//
// 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 "mlir/Dialect/QuantOps/FakeQuantSupport.h"
#include "mlir/Dialect/QuantOps/QuantTypes.h"

using namespace mlir;
using namespace mlir::quant;

UniformQuantizedType
mlir::quant::fakeQuantAttrsToType(Location loc, unsigned numBits, double rmin,
                                  double rmax, bool narrowRange,
                                  Type expressedType, bool isSigned) {
  MLIRContext *ctx = expressedType.getContext();
  Type storageType;
  unsigned flags;
  int64_t qmin;
  int64_t qmax;

  // Hard-coded type mapping from TFLite.
  if (numBits <= 8) {
    storageType = IntegerType::get(8, ctx);
    if (isSigned) {
      flags = QuantizationFlags::Signed;
      qmin = -128;
      qmax = 127;
    } else {
      flags = 0;
      qmin = 0;
      qmax = 255;
    }
  } else if (numBits <= 16) {
    storageType = IntegerType::get(16, ctx);
    if (isSigned) {
      flags = QuantizationFlags::Signed;
      qmin = -32768;
      qmax = 32767;
    } else {
      flags = 0;
      qmin = 0;
      qmax = 65535;
    }
  } else {
    emitError(loc, "unsupported FakeQuant number of bits: ") << numBits;
    return nullptr;
  }

  // Handle narrowRange.
  if (narrowRange) {
    qmin += 1;
  }

  // Range must straddle zero.
  if (rmin > 0.0 || rmax < 0.0) {
    return (emitError(loc, "FakeQuant range must straddle zero: [")
                << rmin << "," << rmax << "]",
            nullptr);
  }

  // Special case where min/max is close enough. The tensor contents are all
  // 0.0s, so the scale is set to 1.0 and the tensor can be quantized to zero
  // points and dequantized to 0.0.
  if (std::fabs(rmax - rmin) < std::numeric_limits<double>::epsilon()) {
    return UniformQuantizedType::getChecked(flags, storageType, expressedType,
                                            1.0, 0, qmin, qmax, loc);
  }

  // Determine the scale.
  const double qminDouble = qmin;
  const double qmaxDouble = qmax;
  const double scale = (rmax - rmin) / (qmaxDouble - qminDouble);

  // Zero point computation.
  // In float, solve the affine equation for any known pair
  // (real value, corresponding quantized value), of which, two such pairs
  // are known: (rmin, qmin), (rmax, qmax).
  // The arithmetic error on the zero point computed from either pair will be
  // roughly machine_epsilon * (sum of absolute values of terms).
  // Use the variant that adds the smaller error.
  const double zeroPointFromMin = qminDouble - rmin / scale;
  const double zeroPointFromMinError =
      std::abs(qminDouble) + std::abs(rmin / scale);
  const double zeroPointFromMax = qmaxDouble - rmax / scale;
  const double zeroPointFromMaxError =
      std::abs(qmaxDouble) + std::abs(rmax / scale);

  const double zeroPointDouble = (zeroPointFromMinError < zeroPointFromMaxError)
                                     ? zeroPointFromMin
                                     : zeroPointFromMax;

  // Now nudge the zero point to be an integer.
  int64_t nudgedZeroPoint = 0;
  if (zeroPointDouble < qminDouble) {
    nudgedZeroPoint = qmin;
  } else if (zeroPointDouble > qmaxDouble) {
    nudgedZeroPoint = qmax;
  } else {
    nudgedZeroPoint = round(zeroPointDouble);
  }

  // By construction, the nudged zero point should always be in range.
  assert(nudgedZeroPoint >= qmin);
  assert(nudgedZeroPoint <= qmax);

  return UniformQuantizedType::getChecked(flags, storageType, expressedType,
                                          scale, nudgedZeroPoint, qmin, qmax,
                                          loc);
}
<|endoftext|>


<commit_before>
#include <QApplication>
#include <QBoxLayout>
#include <QMenuBar>
#include <QSignalMapper>
#include <QTimer>

#include <sstream>  //include this to use string streams

#include "wd.h"
#include "font.h"
#include "cmd.h"
#include "form.h"
#include "pane.h"
#include "tabs.h"
#include "child.h"
#include "isigraph2.h"
#include "menus.h"
#ifndef QT_NO_OPENGL
#include "opengl2.h"
#endif
#include "../base/state.h"
#include "../base/term.h"

extern bool standAlone;

// ---------------------------------------------------------------------
Form::Form(string s, string p, string loc, QWidget *parent) : QWidget (parent)
{
  Q_UNUSED(parent);
  id=s;
  child=0;
  evtchild=0;
  locale=loc;
  menubar=0;
  tab=0;
  closed=false;
  shown=false;
  setAttribute(Qt::WA_DeleteOnClose);
  QStringList m=s2q(p).split(' ',QString::SkipEmptyParts);
  escclose=m.contains("escclose");
  closeok=m.contains("closeok");
  setpn(s);

  Qt::WindowFlags flags=0;
  if (m.contains("dialog")) flags=Qt::Dialog|Qt::WindowTitleHint|Qt::WindowStaysOnTopHint|Qt::CustomizeWindowHint;
  if (m.contains("popup")) flags=Qt::Popup;
  if (m.contains("minbutton")) flags|=Qt::WindowMinimizeButtonHint;
  if (m.contains("maxbutton")) flags|=Qt::WindowMaximizeButtonHint;
  if (m.contains("closebutton")) flags|=Qt::WindowCloseButtonHint;
  if (m.contains("ptop")) flags=Qt::WindowStaysOnTopHint;
  if (m.contains("owner")) {
    flags|=Qt::Window;
    setWindowModality(Qt::WindowModal);
  }
  setWindowFlags(flags);

  layout=new QVBoxLayout(this);
  setpadding(0,0,0,0);
  layout->setSpacing(0);
  if (m.contains("nosize")) layout->setSizeConstraint( QLayout::SetFixedSize );
  addpane(0);
  signalMapper=new QSignalMapper(this);
  connect(signalMapper,SIGNAL(mapped(QWidget *)),
          this,SLOT(buttonClicked(QWidget *)));
  timer=new QTimer;
  connect(timer, SIGNAL(timeout()),this,SLOT(systimer()));
}

// ---------------------------------------------------------------------
Form::~Form()
{
  Child *n;
  for (int i=0; i<children.size(); i++)
    if ("isigraph0"==(n=children.at(i))->type) {
      if ((Isigraph2*)n->widget) {
        if (((Isigraph2*)n->widget)->painter) {
          ((Isigraph2*)n->widget)->painter->end();
          delete ((Isigraph2*)n->widget)->painter;
          ((Isigraph2*)n->widget)->painter=0;
        }
      }
#ifndef QT_NO_OPENGL
    } else if ("opengl"==(n=children.at(i))->type) {
      if ((Opengl2*)n->widget) {
        if (((Opengl2*)n->widget)->painter) {
          ((Opengl2*)n->widget)->painter->end();
          delete ((Opengl2*)n->widget)->painter;
          ((Opengl2*)n->widget)->painter=0;
        }
      }
#endif
    }
  if (this==form) form = 0;
  if (this==evtform) evtform = 0;
  if (timer) delete timer;
  Forms.removeOne(this);
  if (Forms.isEmpty()) form=0;
#ifdef QT_OS_ANDROID
  if (!Forms.isEmpty()) {
    form=Forms.last();
    wdactivateform();
  }
#endif
  if (Forms.isEmpty() && (!ShowIde)) {
#ifdef QT_OS_ANDROID
    state_quit();
    QApplication::quit();
#else
    if (standAlone) {
      state_quit();
      QApplication::quit();
    } else {
      showide(true);
      if (ShowIde) {
        term->activateWindow();
        term->raise();
        term->repaint();
      }
    }
#endif
  }
}

// ---------------------------------------------------------------------
void Form::addchild(Child *c)
{
  child=c;
  children.append(c);
}

// ---------------------------------------------------------------------
void Form::addmenu()
{
  menubar= new Menus("menu","",this,0);
  addchild((Child *) menubar);
  layout->insertWidget(0,child->widget);
}

// ---------------------------------------------------------------------
Pane *Form::addpane(int n)
{
  pane=new Pane(n,this);
  panes.append(pane);
  return pane;
}

// ---------------------------------------------------------------------
void Form::buttonClicked(QWidget *w)
{
  Child *child=(Child *) w;
  child->event="button";
  signalevent(child);
}

// ---------------------------------------------------------------------
void Form::closeEvent(QCloseEvent *e)
{
  if (closeok || closed) {
    e->accept();
    return;
  }
  e->ignore();
  event="close";
  fakeid="";
  form=this;
  signalevent(0);
  if (closed) {
    e->accept();
  } else e->ignore();
}

// ---------------------------------------------------------------------
// close if not the main pane
void Form::closepane()
{
  if (panes.size()<=1) return;
  panes.removeLast();
  pane=panes.last();
}

// ---------------------------------------------------------------------
string Form::getsysmodifiers()
{
  Qt::KeyboardModifiers mod = QApplication::keyboardModifiers();
  return i2s((mod.testFlag(Qt::ShiftModifier) ? 1 : 0) +
             (mod.testFlag(Qt::ControlModifier)? 2 : 0));
}

// ---------------------------------------------------------------------
string Form::hschild()
{
  return p2s((void *)child);
}

// ---------------------------------------------------------------------
string Form::hsform()
{
  return p2s((void *)this);
}

// ---------------------------------------------------------------------
string Form::qform()
{
  return i2s(pos().x())+" "+i2s(pos().y())+" "+i2s(size().width())+" "+i2s(size().height());
}

// ---------------------------------------------------------------------
Child *Form::id2child(string n)
{
  for (int i=0; i<children.size(); i++)
    if ("menu"!=children.at(i)->type && n==children.at(i)->id)
      return children.at(i);
  return (Child *) 0;
}

// ---------------------------------------------------------------------
bool Form::ischild(Child *n)
{
  return children.contains(n);
}

// ---------------------------------------------------------------------
void Form::keyPressEvent(QKeyEvent *e)
{
  int k=e->key();
#ifdef QT_OS_ANDROID
  if (k==Qt::Key_Back) {
    QWidget::keyPressEvent(e);
    return;
  }
#endif
  if (k==Qt::Key_Escape) {
    e->ignore();
    if (closed) return;
    if (escclose) {
      if (closeok) {
        closed=true;
        close();
      } else  {
        event="close";
        fakeid="";
        form=this;
        signalevent(0);
      }
    } else {
      event="cancel";
      fakeid="";
      form=this;
      signalevent(0);
    }
  } else if (k>=Qt::Key_F1 && k<=Qt::Key_F35) {
    event="fkey";
    form=this;
    signalevent(0,e);
  } else if (k>=Qt::Key_A && k<=Qt::Key_Z && (e->modifiers() & Qt::ControlModifier)) {
    event="fkey";
    form=this;
    signalevent(0,e);
  } else
    QWidget::keyPressEvent(e);
}

// ---------------------------------------------------------------------
void Form::keyReleaseEvent(QKeyEvent *e)
{
#ifdef QT_OS_ANDROID
  if (e->key()==Qt::Key_Back) {
    if (closed) return;
    if (closeok) {
      closed=true;
      close();
    } else {
      event="close";
      fakeid="";
      form=this;
      signalevent(0);
    }
  } else QWidget::keyReleaseEvent(e);
#else
  QWidget::keyReleaseEvent(e);
#endif
}

// ---------------------------------------------------------------------
Child *Form::setmenuid(string id)
{
  if (menubar && menubar->items.contains(s2q(id)))
    return (Child *) menubar;
  return (Child *) 0;
}

// ---------------------------------------------------------------------
void Form::setpadding(int l,int t,int r,int b)
{
  layout->setContentsMargins(l,t,r,b);
}

// ---------------------------------------------------------------------
void Form::setpn(string p)
{
  setWindowTitle(s2q(p));
}

// ---------------------------------------------------------------------
void Form::setpicon(string p)
{
  QString s=s2q(p);
  setWindowIcon(QIcon(s));
}

// ---------------------------------------------------------------------
void Form::settimer(string p)
{
  int n=c_strtoi(p);
  if (n)
    timer->start(n);
  else
    timer->stop();
}

// ---------------------------------------------------------------------
void Form::showit()
{
#ifdef QT_OS_ANDROID
// showide(false);
  if (Forms.size()>1)
    (Forms.at(Forms.size()-2))->setVisible(false);
#endif
  for (int i=tabs.size()-1; i>=0; i--)
    tabs.last()->tabend();
  for (int i=panes.size()-1; i>=0; i--)
    panes.last()->fini();
  layout->addWidget(pane);
  setLayout(layout);
  show();

  shown=true;
}

// ---------------------------------------------------------------------
void Form::signalevent(Child *c, QKeyEvent *e)
{
  if (NoEvents || closed) return;
  string loc = locale;
  evtform=this;
  if (c) {
    evtchild=c;
    c->setform();
    sysmodifiers=c->sysmodifiers;
    if (sysmodifiers.empty())
      sysmodifiers=getsysmodifiers();
    sysdata=c->sysdata;
    loc = (""!=c->locale)?c->locale:locale;
  } else {
    evtchild=0;
    if (event=="fkey") {
      int k=e->key();
      if (k>=Qt::Key_A && k<=Qt::Key_Z && (e->modifiers() & Qt::ControlModifier)) {
        fakeid=(char)e->key()+32;  // lower case
        fakeid=fakeid + "ctrl" + string( (e->modifiers() & Qt::ShiftModifier) ? "shift" : "" );
      } else if (k>=Qt::Key_F1 && k<=Qt::Key_F35) {
        ostringstream ostr;
        ostr << e->key()+1-Qt::Key_F1;
        fakeid="f"+ ostr.str() + string((e->modifiers() & Qt::ControlModifier) ? "ctrl" : "") + string((e->modifiers() & Qt::ShiftModifier) ? "shift" : "");
      }
    }
  }
  var_cmddo("(i.0 0)\"_ wdhandler_" + s2q(loc) + "_$0");
}

// ---------------------------------------------------------------------
string Form::state(int evt)
{
  string c,c1,e,r,s,ec;

  if (evt) {
    if (evtchild) {
      c=evtchild->eid;
      e=evtchild->event;
      ec=evtchild->locale;
    } else {
      c=fakeid;
      e=event;
    }
    c1=(c.empty()) ? string("") : (c+"_") ;
    r+=spair("syshandler",id+"_handler");
    r+=spair("sysevent",id+"_"+c1+e);
    r+=spair("sysdefault",id+"_default");
    r+=spair("sysparent",id);
    r+=spair("syschild",c);
    r+=spair("systype",e);
    r+=spair("syslocalec",ec);
  }

  // need only syslocale (not syslocalep, syslocalec)?...  in isigraph
  r+=spair("syslocalep",locale);
  r+=spair("syshwndp",hsform());
  r+=spair("syshwndc",hschild());
  r+=spair("syslastfocus",(string)"");
  r+=spair("sysmodifiers",sysmodifiers);
  r+=spair("sysdata",sysdata);

  for (int i=0; i<children.size(); i++)
    s+=children.at(i)->state();

  return r+s;
}

// ---------------------------------------------------------------------
// for debugging
void Form::status(string s)
{
  qDebug() << "form status: " << s2q(s);
  qDebug() << "current pane, panes: " << pane << panes;
  qDebug() << "current tab, tabs: " << tab << tabs;
}

// ---------------------------------------------------------------------
void Form::systimer()
{
  event="timer";
  signalevent(0);
}
<commit_msg>set fakeid for form timer event<commit_after>
#include <QApplication>
#include <QBoxLayout>
#include <QMenuBar>
#include <QSignalMapper>
#include <QTimer>

#include <sstream>  //include this to use string streams

#include "wd.h"
#include "font.h"
#include "cmd.h"
#include "form.h"
#include "pane.h"
#include "tabs.h"
#include "child.h"
#include "isigraph2.h"
#include "menus.h"
#ifndef QT_NO_OPENGL
#include "opengl2.h"
#endif
#include "../base/state.h"
#include "../base/term.h"

extern bool standAlone;

// ---------------------------------------------------------------------
Form::Form(string s, string p, string loc, QWidget *parent) : QWidget (parent)
{
  Q_UNUSED(parent);
  id=s;
  child=0;
  evtchild=0;
  locale=loc;
  menubar=0;
  tab=0;
  closed=false;
  shown=false;
  setAttribute(Qt::WA_DeleteOnClose);
  QStringList m=s2q(p).split(' ',QString::SkipEmptyParts);
  escclose=m.contains("escclose");
  closeok=m.contains("closeok");
  setpn(s);

  Qt::WindowFlags flags=0;
  if (m.contains("dialog")) flags=Qt::Dialog|Qt::WindowTitleHint|Qt::WindowStaysOnTopHint|Qt::CustomizeWindowHint;
  if (m.contains("popup")) flags=Qt::Popup;
  if (m.contains("minbutton")) flags|=Qt::WindowMinimizeButtonHint;
  if (m.contains("maxbutton")) flags|=Qt::WindowMaximizeButtonHint;
  if (m.contains("closebutton")) flags|=Qt::WindowCloseButtonHint;
  if (m.contains("ptop")) flags=Qt::WindowStaysOnTopHint;
  if (m.contains("owner")) {
    flags|=Qt::Window;
    setWindowModality(Qt::WindowModal);
  }
  setWindowFlags(flags);

  layout=new QVBoxLayout(this);
  setpadding(0,0,0,0);
  layout->setSpacing(0);
  if (m.contains("nosize")) layout->setSizeConstraint( QLayout::SetFixedSize );
  addpane(0);
  signalMapper=new QSignalMapper(this);
  connect(signalMapper,SIGNAL(mapped(QWidget *)),
          this,SLOT(buttonClicked(QWidget *)));
  timer=new QTimer;
  connect(timer, SIGNAL(timeout()),this,SLOT(systimer()));
}

// ---------------------------------------------------------------------
Form::~Form()
{
  Child *n;
  for (int i=0; i<children.size(); i++)
    if ("isigraph0"==(n=children.at(i))->type) {
      if ((Isigraph2*)n->widget) {
        if (((Isigraph2*)n->widget)->painter) {
          ((Isigraph2*)n->widget)->painter->end();
          delete ((Isigraph2*)n->widget)->painter;
          ((Isigraph2*)n->widget)->painter=0;
        }
      }
#ifndef QT_NO_OPENGL
    } else if ("opengl"==(n=children.at(i))->type) {
      if ((Opengl2*)n->widget) {
        if (((Opengl2*)n->widget)->painter) {
          ((Opengl2*)n->widget)->painter->end();
          delete ((Opengl2*)n->widget)->painter;
          ((Opengl2*)n->widget)->painter=0;
        }
      }
#endif
    }
  if (this==form) form = 0;
  if (this==evtform) evtform = 0;
  if (timer) delete timer;
  Forms.removeOne(this);
  if (Forms.isEmpty()) form=0;
#ifdef QT_OS_ANDROID
  if (!Forms.isEmpty()) {
    form=Forms.last();
    wdactivateform();
  }
#endif
  if (Forms.isEmpty() && (!ShowIde)) {
#ifdef QT_OS_ANDROID
    state_quit();
    QApplication::quit();
#else
    if (standAlone) {
      state_quit();
      QApplication::quit();
    } else {
      showide(true);
      if (ShowIde) {
        term->activateWindow();
        term->raise();
        term->repaint();
      }
    }
#endif
  }
}

// ---------------------------------------------------------------------
void Form::addchild(Child *c)
{
  child=c;
  children.append(c);
}

// ---------------------------------------------------------------------
void Form::addmenu()
{
  menubar= new Menus("menu","",this,0);
  addchild((Child *) menubar);
  layout->insertWidget(0,child->widget);
}

// ---------------------------------------------------------------------
Pane *Form::addpane(int n)
{
  pane=new Pane(n,this);
  panes.append(pane);
  return pane;
}

// ---------------------------------------------------------------------
void Form::buttonClicked(QWidget *w)
{
  Child *child=(Child *) w;
  child->event="button";
  signalevent(child);
}

// ---------------------------------------------------------------------
void Form::closeEvent(QCloseEvent *e)
{
  if (closeok || closed) {
    e->accept();
    return;
  }
  e->ignore();
  event="close";
  fakeid="";
  form=this;
  signalevent(0);
  if (closed) {
    e->accept();
  } else e->ignore();
}

// ---------------------------------------------------------------------
// close if not the main pane
void Form::closepane()
{
  if (panes.size()<=1) return;
  panes.removeLast();
  pane=panes.last();
}

// ---------------------------------------------------------------------
string Form::getsysmodifiers()
{
  Qt::KeyboardModifiers mod = QApplication::keyboardModifiers();
  return i2s((mod.testFlag(Qt::ShiftModifier) ? 1 : 0) +
             (mod.testFlag(Qt::ControlModifier)? 2 : 0));
}

// ---------------------------------------------------------------------
string Form::hschild()
{
  return p2s((void *)child);
}

// ---------------------------------------------------------------------
string Form::hsform()
{
  return p2s((void *)this);
}

// ---------------------------------------------------------------------
string Form::qform()
{
  return i2s(pos().x())+" "+i2s(pos().y())+" "+i2s(size().width())+" "+i2s(size().height());
}

// ---------------------------------------------------------------------
Child *Form::id2child(string n)
{
  for (int i=0; i<children.size(); i++)
    if ("menu"!=children.at(i)->type && n==children.at(i)->id)
      return children.at(i);
  return (Child *) 0;
}

// ---------------------------------------------------------------------
bool Form::ischild(Child *n)
{
  return children.contains(n);
}

// ---------------------------------------------------------------------
void Form::keyPressEvent(QKeyEvent *e)
{
  int k=e->key();
#ifdef QT_OS_ANDROID
  if (k==Qt::Key_Back) {
    QWidget::keyPressEvent(e);
    return;
  }
#endif
  if (k==Qt::Key_Escape) {
    e->ignore();
    if (closed) return;
    if (escclose) {
      if (closeok) {
        closed=true;
        close();
      } else  {
        event="close";
        fakeid="";
        form=this;
        signalevent(0);
      }
    } else {
      event="cancel";
      fakeid="";
      form=this;
      signalevent(0);
    }
  } else if (k>=Qt::Key_F1 && k<=Qt::Key_F35) {
    event="fkey";
    form=this;
    signalevent(0,e);
  } else if (k>=Qt::Key_A && k<=Qt::Key_Z && (e->modifiers() & Qt::ControlModifier)) {
    event="fkey";
    form=this;
    signalevent(0,e);
  } else
    QWidget::keyPressEvent(e);
}

// ---------------------------------------------------------------------
void Form::keyReleaseEvent(QKeyEvent *e)
{
#ifdef QT_OS_ANDROID
  if (e->key()==Qt::Key_Back) {
    if (closed) return;
    if (closeok) {
      closed=true;
      close();
    } else {
      event="close";
      fakeid="";
      form=this;
      signalevent(0);
    }
  } else QWidget::keyReleaseEvent(e);
#else
  QWidget::keyReleaseEvent(e);
#endif
}

// ---------------------------------------------------------------------
Child *Form::setmenuid(string id)
{
  if (menubar && menubar->items.contains(s2q(id)))
    return (Child *) menubar;
  return (Child *) 0;
}

// ---------------------------------------------------------------------
void Form::setpadding(int l,int t,int r,int b)
{
  layout->setContentsMargins(l,t,r,b);
}

// ---------------------------------------------------------------------
void Form::setpn(string p)
{
  setWindowTitle(s2q(p));
}

// ---------------------------------------------------------------------
void Form::setpicon(string p)
{
  QString s=s2q(p);
  setWindowIcon(QIcon(s));
}

// ---------------------------------------------------------------------
void Form::settimer(string p)
{
  int n=c_strtoi(p);
  if (n)
    timer->start(n);
  else
    timer->stop();
}

// ---------------------------------------------------------------------
void Form::showit()
{
#ifdef QT_OS_ANDROID
// showide(false);
  if (Forms.size()>1)
    (Forms.at(Forms.size()-2))->setVisible(false);
#endif
  for (int i=tabs.size()-1; i>=0; i--)
    tabs.last()->tabend();
  for (int i=panes.size()-1; i>=0; i--)
    panes.last()->fini();
  layout->addWidget(pane);
  setLayout(layout);
  show();

  shown=true;
}

// ---------------------------------------------------------------------
void Form::signalevent(Child *c, QKeyEvent *e)
{
  if (NoEvents || closed) return;
  string loc = locale;
  evtform=this;
  if (c) {
    evtchild=c;
    c->setform();
    sysmodifiers=c->sysmodifiers;
    if (sysmodifiers.empty())
      sysmodifiers=getsysmodifiers();
    sysdata=c->sysdata;
    loc = (""!=c->locale)?c->locale:locale;
  } else {
    evtchild=0;
    if (event=="fkey") {
      int k=e->key();
      if (k>=Qt::Key_A && k<=Qt::Key_Z && (e->modifiers() & Qt::ControlModifier)) {
        fakeid=(char)e->key()+32;  // lower case
        fakeid=fakeid + "ctrl" + string( (e->modifiers() & Qt::ShiftModifier) ? "shift" : "" );
      } else if (k>=Qt::Key_F1 && k<=Qt::Key_F35) {
        ostringstream ostr;
        ostr << e->key()+1-Qt::Key_F1;
        fakeid="f"+ ostr.str() + string((e->modifiers() & Qt::ControlModifier) ? "ctrl" : "") + string((e->modifiers() & Qt::ShiftModifier) ? "shift" : "");
      }
    }
  }
  var_cmddo("(i.0 0)\"_ wdhandler_" + s2q(loc) + "_$0");
}

// ---------------------------------------------------------------------
string Form::state(int evt)
{
  string c,c1,e,r,s,ec;

  if (evt) {
    if (evtchild) {
      c=evtchild->eid;
      e=evtchild->event;
      ec=evtchild->locale;
    } else {
      c=fakeid;
      e=event;
    }
    c1=(c.empty()) ? string("") : (c+"_") ;
    r+=spair("syshandler",id+"_handler");
    r+=spair("sysevent",id+"_"+c1+e);
    r+=spair("sysdefault",id+"_default");
    r+=spair("sysparent",id);
    r+=spair("syschild",c);
    r+=spair("systype",e);
    r+=spair("syslocalec",ec);
  }

  // need only syslocale (not syslocalep, syslocalec)?...  in isigraph
  r+=spair("syslocalep",locale);
  r+=spair("syshwndp",hsform());
  r+=spair("syshwndc",hschild());
  r+=spair("syslastfocus",(string)"");
  r+=spair("sysmodifiers",sysmodifiers);
  r+=spair("sysdata",sysdata);

  for (int i=0; i<children.size(); i++)
    s+=children.at(i)->state();

  return r+s;
}

// ---------------------------------------------------------------------
// for debugging
void Form::status(string s)
{
  qDebug() << "form status: " << s2q(s);
  qDebug() << "current pane, panes: " << pane << panes;
  qDebug() << "current tab, tabs: " << tab << tabs;
}

// ---------------------------------------------------------------------
void Form::systimer()
{
  event="timer";
  fakeid="";
  signalevent(0);
}
<|endoftext|>


<commit_before>/** @file

    Class for handling "views" of a buffer. Views presume the memory for the buffer is managed
    elsewhere and allow efficient access to segments of the buffer without copies. Views are read
    only as the view doesn't own the memory. Along with generic buffer methods are specialized
    methods to support better string parsing, particularly token based parsing.

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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 <ts/MemView.h>
#include <sstream>
#include <cctype>
#include <ts/ink_platform.h>

namespace ts
{
StringView::StringView(const char *s) : _ptr(s), _size(strlen(s))
{
}

int
memcmp(MemView const &lhs, MemView const &rhs)
{
  int zret;
  size_t n;

  // Seems a bit ugly but size comparisons must be done anyway to get the memcmp args.
  if (lhs.size() < rhs.size()) {
    zret = 1, n = lhs.size();
  } else {
    n    = rhs.size();
    zret = rhs.size() < lhs.size() ? -1 : 0;
  }

  int r = ::memcmp(lhs.ptr(), rhs.ptr(), n);
  if (0 != r) { // If we got a not-equal, override the size based result.
    zret = r;
  }

  return zret;
}

int
strcasecmp(StringView lhs, StringView rhs)
{
  while (lhs && rhs) {
    char l = tolower(*lhs);
    char r = tolower(*rhs);
    if (l < r) {
      return -1;
    } else if (r < l) {
      return 1;
    }
    ++lhs, ++rhs;
  }
  return lhs ? 1 : rhs ? -1 : 0;
}

intmax_t
svtoi(StringView src, StringView *out, int base)
{
  static const int8_t convert[256] = {
    /* [can't do this nicely because clang format won't allow exdented comments]
     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  -1, -1, -1, -1, -1, -1, // 30
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 40
    25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 50
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 60
    25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 70
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // B0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // D0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // F0
  };

  intmax_t zret = 0;

  if (out) {
    out->clear();
  }
  if (!(1 < base && base <= 36)) {
    return 0;
  }
  if (src.ltrim(&isspace)) {
    const char *start = src.ptr();
    int8_t v;
    bool neg = false;
    if ('-' == *src) {
      ++src;
      neg = true;
    }
    while (src.size() && (-1 != (v = convert[static_cast<unsigned char>(*src)]))) {
      zret = zret * base + v;
      ++src;
    }
    if (out && (src.ptr() > (neg ? start + 1 : start))) {
      out->setView(start, src.ptr());
    }

    if (neg) {
      zret = -zret;
    }
  }
  return zret;
}

// Do the template instantions.
template void detail::stream_fill(std::ostream &, std::size_t);
template std::ostream &StringView::stream_write(std::ostream &, const StringView &) const;
}

namespace std
{
ostream &
operator<<(ostream &os, const ts::MemView &b)
{
  if (os.good()) {
    ostringstream out;
    out << b.size() << '@' << hex << b.ptr();
    os << out.str();
  }
  return os;
}

ostream &
operator<<(ostream &os, const ts::StringView &b)
{
  if (os.good()) {
    b.stream_write<ostream>(os, b);
    os.width(0);
  }
  return os;
}
}
<commit_msg>Fix StringView constructor to not fail on a nulll pointer.<commit_after>/** @file

    Class for handling "views" of a buffer. Views presume the memory for the buffer is managed
    elsewhere and allow efficient access to segments of the buffer without copies. Views are read
    only as the view doesn't own the memory. Along with generic buffer methods are specialized
    methods to support better string parsing, particularly token based parsing.

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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 <ts/MemView.h>
#include <sstream>
#include <cctype>
#include <ts/ink_platform.h>

namespace ts
{
StringView::StringView(const char *s) : _ptr(s), _size(s ? strlen(s) : 0)
{
}

int
memcmp(MemView const &lhs, MemView const &rhs)
{
  int zret;
  size_t n;

  // Seems a bit ugly but size comparisons must be done anyway to get the memcmp args.
  if (lhs.size() < rhs.size()) {
    zret = 1, n = lhs.size();
  } else {
    n    = rhs.size();
    zret = rhs.size() < lhs.size() ? -1 : 0;
  }

  int r = ::memcmp(lhs.ptr(), rhs.ptr(), n);
  if (0 != r) { // If we got a not-equal, override the size based result.
    zret = r;
  }

  return zret;
}

int
strcasecmp(StringView lhs, StringView rhs)
{
  while (lhs && rhs) {
    char l = tolower(*lhs);
    char r = tolower(*rhs);
    if (l < r) {
      return -1;
    } else if (r < l) {
      return 1;
    }
    ++lhs, ++rhs;
  }
  return lhs ? 1 : rhs ? -1 : 0;
}

intmax_t
svtoi(StringView src, StringView *out, int base)
{
  static const int8_t convert[256] = {
    /* [can't do this nicely because clang format won't allow exdented comments]
     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  -1, -1, -1, -1, -1, -1, // 30
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 40
    25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 50
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 60
    25, 26, 27, 28, 20, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 70
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // B0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // D0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // F0
  };

  intmax_t zret = 0;

  if (out) {
    out->clear();
  }
  if (!(1 < base && base <= 36)) {
    return 0;
  }
  if (src.ltrim(&isspace)) {
    const char *start = src.ptr();
    int8_t v;
    bool neg = false;
    if ('-' == *src) {
      ++src;
      neg = true;
    }
    while (src.size() && (-1 != (v = convert[static_cast<unsigned char>(*src)]))) {
      zret = zret * base + v;
      ++src;
    }
    if (out && (src.ptr() > (neg ? start + 1 : start))) {
      out->setView(start, src.ptr());
    }

    if (neg) {
      zret = -zret;
    }
  }
  return zret;
}

// Do the template instantions.
template void detail::stream_fill(std::ostream &, std::size_t);
template std::ostream &StringView::stream_write(std::ostream &, const StringView &) const;
}

namespace std
{
ostream &
operator<<(ostream &os, const ts::MemView &b)
{
  if (os.good()) {
    ostringstream out;
    out << b.size() << '@' << hex << b.ptr();
    os << out.str();
  }
  return os;
}

ostream &
operator<<(ostream &os, const ts::StringView &b)
{
  if (os.good()) {
    b.stream_write<ostream>(os, b);
    os.width(0);
  }
  return os;
}
}
<|endoftext|>