<commit_before>// Copyright 2020 The gVisor 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 <poll.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include <array>
#include <string>

#include "gtest/gtest.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"

namespace gvisor {
namespace testing {

// Test fixture for tests that apply to pairs of connected sockets.
using ConnectStressTest = SocketPairTest;

TEST_P(ConnectStressTest, Reset) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    const std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

    // Send some data to ensure that the connection gets reset and the port gets
    // released immediately. This avoids either end entering TIME-WAIT.
    char sent_data[100] = {};
    ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
                SyscallSucceedsWithValue(sizeof(sent_data)));
    // Poll the other FD to make sure that the data is in the receive buffer
    // before closing it to ensure a RST is triggered.
    const int kTimeout = 10000;
    struct pollfd pfd = {
        .fd = sockets->second_fd(),
        .events = POLL_IN,
    };
    ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
  }
}

// Tests that opening too many connections -- without closing them -- does lead
// to port exhaustion.
TEST_P(ConnectStressTest, TooManyOpen) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  int err_num = 0;
  std::vector<std::unique_ptr<SocketPair>> sockets =
      std::vector<std::unique_ptr<SocketPair>>(nports);
  for (int i = 0; i < nports * 2; i++) {
    PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
    if (!socks.ok()) {
      err_num = socks.error().errno_value();
      break;
    }
    sockets.push_back(std::move(socks).ValueOrDie());
  }
  ASSERT_EQ(err_num, EADDRINUSE);
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, ConnectStressTest,
    ::testing::Values(IPv6UDPBidirectionalBindSocketPair(0),
                      IPv4UDPBidirectionalBindSocketPair(0),
                      DualStackUDPBidirectionalBindSocketPair(0),

                      // Without REUSEADDR, we get port exhaustion on Linux.
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv6TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv4TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
                          DualStackTCPAcceptBindSocketPair(0))));

// Test fixture for tests that apply to pairs of connected sockets created with
// a persistent listener (if applicable).
class PersistentListenerConnectStressTest : public SocketPairTest {
 protected:
  PersistentListenerConnectStressTest() : slept_{false} {}

  // NewSocketSleep is the same as NewSocketPair, but will sleep once (over the
  // lifetime of the fixture) and retry if creation fails due to EADDRNOTAVAIL.
  PosixErrorOr<std::unique_ptr<SocketPair>> NewSocketSleep() {
    // We can't reuse a connection too close in time to its last use, as TCP
    // uses the timestamp difference to disambiguate connections. With a
    // sufficiently small port range, we'll cycle through too quickly, and TCP
    // won't allow for connection reuse. Thus, we sleep the first time
    // encountering EADDRINUSE to allow for that difference (1 second in
    // gVisor).
    PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
    if (socks.ok()) {
      return socks;
    }
    if (!slept_ && socks.error().errno_value() == EADDRNOTAVAIL) {
      absl::SleepFor(absl::Milliseconds(1500));
      slept_ = true;
      return NewSocketPair();
    }
    return socks;
  }

 private:
  bool slept_;
};

TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the second_fd. This ensures that the first_fd
      // enters TIME-WAIT and not second_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->second_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    const std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the first_fd. This ensures that the second_fd
      // enters TIME-WAIT and not first_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->first_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, Close) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
  }
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, PersistentListenerConnectStressTest,
    ::testing::Values(
        IPv6UDPBidirectionalBindSocketPair(0),
        IPv4UDPBidirectionalBindSocketPair(0),
        DualStackUDPBidirectionalBindSocketPair(0),

        // Without REUSEADDR, we get port exhaustion on Linux.
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv6TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv4TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            DualStackTCPAcceptBindPersistentListenerSocketPair(0))));

using DataTransferStressTest = SocketPairTest;

TEST_P(DataTransferStressTest, BigDataTransfer) {
  // TODO(b/165912341): These are too slow on KVM platform with nested virt.
  SKIP_IF(GvisorPlatform() == Platform::kKVM);

  const std::unique_ptr<SocketPair> sockets =
      ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  int client_fd = sockets->first_fd();
  int server_fd = sockets->second_fd();

  ScopedThread echo([server_fd]() {
    std::array<uint8_t, 1024> buf;
    for (;;) {
      ssize_t r = read(server_fd, buf.data(), buf.size());
      ASSERT_THAT(r, SyscallSucceeds());
      if (r == 0) {
        break;
      }
      for (ssize_t i = 0; i < r;) {
        ssize_t w = write(server_fd, buf.data() + i, r - i);
        ASSERT_GE(w, 0);
        i += w;
      }
    }
    ASSERT_THAT(shutdown(server_fd, SHUT_WR), SyscallSucceeds());
  });

  const std::string chunk = "Though this upload be but little, it is fierce.";
  std::string big_string;
  while (big_string.size() < 31 << 20) {
    big_string += chunk;
  }
  absl::string_view data = big_string;

  ScopedThread writer([client_fd, data]() {
    absl::string_view view = data;
    while (!view.empty()) {
      ssize_t n = write(client_fd, view.data(), view.size());
      ASSERT_GE(n, 0);
      view = view.substr(n);
    }
    ASSERT_THAT(shutdown(client_fd, SHUT_WR), SyscallSucceeds());
  });

  std::string buf;
  buf.resize(1 << 20);
  while (!data.empty()) {
    ssize_t n = read(client_fd, buf.data(), buf.size());
    ASSERT_GE(n, 0);
    for (ssize_t i = 0; i < n; i += chunk.size()) {
      ssize_t c = std::min(ssize_t(chunk.size()), n - i);
      ASSERT_EQ(buf.substr(i, c), data.substr(i, c)) << "offset " << i;
    }
    data = data.substr(n);
  }
  // Should read EOF now.
  ASSERT_THAT(read(client_fd, buf.data(), buf.size()),
              SyscallSucceedsWithValue(0));
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, DataTransferStressTest,
    ::testing::Values(IPv6TCPAcceptBindPersistentListenerSocketPair(0),
                      IPv4TCPAcceptBindPersistentListenerSocketPair(0),
                      DualStackTCPAcceptBindPersistentListenerSocketPair(0)));

}  // namespace testing
}  // namespace gvisor
<commit_msg>shrink DataTransferStressTest<commit_after>// Copyright 2020 The gVisor 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 <poll.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include <array>
#include <string>

#include "gtest/gtest.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"

namespace gvisor {
namespace testing {

// Test fixture for tests that apply to pairs of connected sockets.
using ConnectStressTest = SocketPairTest;

TEST_P(ConnectStressTest, Reset) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    const std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

    // Send some data to ensure that the connection gets reset and the port gets
    // released immediately. This avoids either end entering TIME-WAIT.
    char sent_data[100] = {};
    ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
                SyscallSucceedsWithValue(sizeof(sent_data)));
    // Poll the other FD to make sure that the data is in the receive buffer
    // before closing it to ensure a RST is triggered.
    const int kTimeout = 10000;
    struct pollfd pfd = {
        .fd = sockets->second_fd(),
        .events = POLL_IN,
    };
    ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
  }
}

// Tests that opening too many connections -- without closing them -- does lead
// to port exhaustion.
TEST_P(ConnectStressTest, TooManyOpen) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  int err_num = 0;
  std::vector<std::unique_ptr<SocketPair>> sockets =
      std::vector<std::unique_ptr<SocketPair>>(nports);
  for (int i = 0; i < nports * 2; i++) {
    PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
    if (!socks.ok()) {
      err_num = socks.error().errno_value();
      break;
    }
    sockets.push_back(std::move(socks).ValueOrDie());
  }
  ASSERT_EQ(err_num, EADDRINUSE);
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, ConnectStressTest,
    ::testing::Values(IPv6UDPBidirectionalBindSocketPair(0),
                      IPv4UDPBidirectionalBindSocketPair(0),
                      DualStackUDPBidirectionalBindSocketPair(0),

                      // Without REUSEADDR, we get port exhaustion on Linux.
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv6TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv4TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
                          DualStackTCPAcceptBindSocketPair(0))));

// Test fixture for tests that apply to pairs of connected sockets created with
// a persistent listener (if applicable).
class PersistentListenerConnectStressTest : public SocketPairTest {
 protected:
  PersistentListenerConnectStressTest() : slept_{false} {}

  // NewSocketSleep is the same as NewSocketPair, but will sleep once (over the
  // lifetime of the fixture) and retry if creation fails due to EADDRNOTAVAIL.
  PosixErrorOr<std::unique_ptr<SocketPair>> NewSocketSleep() {
    // We can't reuse a connection too close in time to its last use, as TCP
    // uses the timestamp difference to disambiguate connections. With a
    // sufficiently small port range, we'll cycle through too quickly, and TCP
    // won't allow for connection reuse. Thus, we sleep the first time
    // encountering EADDRINUSE to allow for that difference (1 second in
    // gVisor).
    PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair();
    if (socks.ok()) {
      return socks;
    }
    if (!slept_ && socks.error().errno_value() == EADDRNOTAVAIL) {
      absl::SleepFor(absl::Milliseconds(1500));
      slept_ = true;
      return NewSocketPair();
    }
    return socks;
  }

 private:
  bool slept_;
};

TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the second_fd. This ensures that the first_fd
      // enters TIME-WAIT and not second_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->second_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    const std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the first_fd. This ensures that the second_fd
      // enters TIME-WAIT and not first_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->first_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, Close) {
  const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
  for (int i = 0; i < nports * 2; i++) {
    std::unique_ptr<SocketPair> sockets =
        ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
  }
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, PersistentListenerConnectStressTest,
    ::testing::Values(
        IPv6UDPBidirectionalBindSocketPair(0),
        IPv4UDPBidirectionalBindSocketPair(0),
        DualStackUDPBidirectionalBindSocketPair(0),

        // Without REUSEADDR, we get port exhaustion on Linux.
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv6TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv4TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            DualStackTCPAcceptBindPersistentListenerSocketPair(0))));

using DataTransferStressTest = SocketPairTest;

TEST_P(DataTransferStressTest, BigDataTransfer) {
  const std::unique_ptr<SocketPair> sockets =
      ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  int client_fd = sockets->first_fd();
  int server_fd = sockets->second_fd();

  ScopedThread echo([server_fd]() {
    std::array<uint8_t, 1024> buf;
    for (;;) {
      ssize_t r = read(server_fd, buf.data(), buf.size());
      ASSERT_THAT(r, SyscallSucceeds());
      if (r == 0) {
        break;
      }
      for (ssize_t i = 0; i < r;) {
        ssize_t w = write(server_fd, buf.data() + i, r - i);
        ASSERT_GE(w, 0);
        i += w;
      }
    }
    ASSERT_THAT(shutdown(server_fd, SHUT_WR), SyscallSucceeds());
  });

  // Tests can be prohibitively slow on the KVM platform with nested virt.
  const int kShift = GvisorPlatform() == Platform::kKVM ? 10 : 20;

  const std::string chunk = "Though this upload be but little, it is fierce.";
  std::string big_string;
  while (big_string.size() < 31 << kShift) {
    big_string += chunk;
  }
  absl::string_view data = big_string;

  ScopedThread writer([client_fd, data]() {
    absl::string_view view = data;
    while (!view.empty()) {
      ssize_t n = write(client_fd, view.data(), view.size());
      ASSERT_GE(n, 0);
      view = view.substr(n);
    }
    ASSERT_THAT(shutdown(client_fd, SHUT_WR), SyscallSucceeds());
  });

  std::string buf;
  buf.resize(1 << kShift);
  while (!data.empty()) {
    ssize_t n = read(client_fd, buf.data(), buf.size());
    ASSERT_GE(n, 0);
    for (ssize_t i = 0; i < n; i += chunk.size()) {
      ssize_t c = std::min(ssize_t(chunk.size()), n - i);
      ASSERT_EQ(buf.substr(i, c), data.substr(i, c)) << "offset " << i;
    }
    data = data.substr(n);
  }
  // Should read EOF now.
  ASSERT_THAT(read(client_fd, buf.data(), buf.size()),
              SyscallSucceedsWithValue(0));
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, DataTransferStressTest,
    ::testing::Values(IPv6TCPAcceptBindPersistentListenerSocketPair(0),
                      IPv4TCPAcceptBindPersistentListenerSocketPair(0),
                      DualStackTCPAcceptBindPersistentListenerSocketPair(0)));

}  // namespace testing
}  // namespace gvisor
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "<WebSig>" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2001, Institute for
 * Data Communications Systems, <http://www.nue.et-inf.uni-siegen.de/>.
 * The development of this software was partly funded by the European
 * Commission in the <WebSig> project in the ISIS Programme.
 * For more information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * XSEC
 *
 * DSIGConstants := Definitions of varius DSIG constants (mainly strings)
 *
 * Author(s): Berin Lautenbach
 *
 * $Id$
 *
 */

#ifndef DSIGCONSTANTS_HEADER
#define DSIGCONSTANTS_HEADER

#include <xsec/utils/XSECSafeBuffer.hpp>

// Xerces
#include <xercesc/util/XMLString.hpp>

XSEC_USING_XERCES(XMLString);

// Name Spaces

#define URI_ID_DSIG		"http://www.w3.org/2000/09/xmldsig#"

// Hashing Algorithms

#define URI_ID_SHA1		"http://www.w3.org/2000/09/xmldsig#sha1"
#define URI_ID_MD5		"http://www.w3.org/2001/04/xmldsig-more#md5"

// Transforms

#define URI_ID_BASE64			"http://www.w3.org/2000/09/xmldsig#base64"
#define URI_ID_XPATH			"http://www.w3.org/TR/1999/REC-xpath-19991116"
#define URI_ID_XSLT				"http://www.w3.org/TR/1999/REC-xslt-19991116"
#define URI_ID_ENVELOPE			"http://www.w3.org/2000/09/xmldsig#enveloped-signature"
#define URI_ID_C14N_NOC			"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
#define URI_ID_C14N_COM			"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
#define URI_ID_EXC_C14N_NOC		"http://www.w3.org/2001/10/xml-exc-c14n#"
#define URI_ID_EXC_C14N_COM		"http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
#define XPATH_EXPR_ENVELOPE		"count(ancestor-or-self::dsig:Signature | \
								 here()/ancestor::dsig:Signature[1]) > \
								 count(ancestor-or-self::dsig:Signature)"

// Signature Algorithms

#define URI_ID_SIG_BASE		"http://www.w3.org/2000/09/xmldsig#"
#define URI_ID_SIG_DSA		"dsa"
#define URI_ID_SIG_HMAC		"hmac"
#define URI_ID_SIG_SHA1		"sha1"
#define URI_ID_SIG_RSA		"rsa"

#define URI_ID_DSA_SHA1		"http://www.w3.org/2000/09/xmldsig#dsa-sha1"
#define URI_ID_HMAC_SHA1	"http://www.w3.org/2000/09/xmldsig#hmac-sha1"
#define URI_ID_RSA_SHA1		"http://www.w3.org/2000/09/xmldsig#rsa-sha1"

// General

#define URI_ID_XMLNS	"http://www.w3.org/2000/xmlns/"
#define URI_ID_MANIFEST "http://www.w3.org/2000/09/xmldsig#Manifest"

// Internal Crypto Providers

#define PROV_OPENSSL	"OpenSSL Provider"
#define PROV_WINCAPI	"WinCAPI Provider"

// Enumerated Types

enum canonicalizationMethod {

	CANON_NONE					= 0,			// No method defined
	CANON_C14N_NOC				= 1,			// C14n without comments
	CANON_C14N_COM				= 2, 			// C14n with comments
	CANON_C14NE_NOC				= 3,			// C14n Exclusive (without comments)
	CANON_C14NE_COM				= 4				// C14n Exlusive (with Comments
};

enum signatureMethod {

	SIGNATURE_NONE				= 0,			// No method defined
	SIGNATURE_DSA				= 1, 			// DSA
	SIGNATURE_HMAC				= 2,			// Hash MAC
	SIGNATURE_RSA				= 3				// RSA
};

enum hashMethod {

	HASH_NONE					= 0,			// No method defined
	HASH_SHA1					= 1, 			// SHA1
	HASH_MD5					= 2,
};

enum transformType {

	TRANSFORM_BASE64,
	TRANSFORM_C14N,
	TRANSFORM_EXC_C14N,
	TRANSFORM_ENVELOPED_SIGNATURE,
	TRANSFORM_XPATH,
	TRANSFORM_XSLT

};

// --------------------------------------------------------------------------------
//           Some utility functions
// --------------------------------------------------------------------------------

inline
bool canonicalizationMethod2URI(safeBuffer &uri, canonicalizationMethod cm) {

	switch (cm) {

	case (CANON_C14N_NOC) :

		uri = URI_ID_C14N_NOC;
		break;

	case (CANON_C14N_COM) :

		uri = URI_ID_C14N_COM;
		break;

	case (CANON_C14NE_NOC) :

		uri = URI_ID_EXC_C14N_NOC;
		break;

	case (CANON_C14NE_COM) :

		uri = URI_ID_EXC_C14N_COM;
		break;

	default :
		return false;		// Unknown type

	}

	return true;

}

inline
bool signatureHashMethod2URI(safeBuffer &uri, signatureMethod sm, hashMethod hm) {

	uri = URI_ID_SIG_BASE;

	switch (sm) {

	case (SIGNATURE_DSA) :

		uri.sbStrcatIn(URI_ID_SIG_DSA);
		break;

	case (SIGNATURE_HMAC) :

		uri.sbStrcatIn(URI_ID_SIG_HMAC);
		break;

	case (SIGNATURE_RSA) :

		uri.sbStrcatIn(URI_ID_SIG_RSA);

	default :

		return false;

	}

	uri.sbStrcatIn("-");

	switch (hm) {

	case (HASH_SHA1) :

		uri.sbStrcatIn(URI_ID_SIG_SHA1);
		break;

	default:

		return false;

	}

	return true;

}

inline
bool hashMethod2URI(safeBuffer &uri, hashMethod hm) {

	switch (hm) {

	case (HASH_SHA1) :

		uri = URI_ID_SHA1;
		break;

	case (HASH_MD5) :

		uri = URI_ID_MD5;
		break;

	default:
		return false;

	}

	return true;

}

// --------------------------------------------------------------------------------
//           Constant Strings Class
// --------------------------------------------------------------------------------

class DSIG_EXPORT DSIGConstants {

public:

	// General strings

	static const XMLCh * const & s_unicodeStrEmpty;		// ""
	static const XMLCh * const & s_unicodeStrNL;			// "\n"
	static const XMLCh * const & s_unicodeStrXmlns;		// "xmlns"

	// DSIG Element Strings
	static const XMLCh * const & s_unicodeStrAlgorithm;

	// URI_IDs
	static const XMLCh * const & s_unicodeStrURIDSIG;
	static const XMLCh * const & s_unicodeStrURISHA1;
	static const XMLCh * const & s_unicodeStrURIMD5;		// Not recommended
	static const XMLCh * const & s_unicodeStrURIBASE64;
	static const XMLCh * const & s_unicodeStrURIXPATH;
	static const XMLCh * const & s_unicodeStrURIXSLT;
	static const XMLCh * const & s_unicodeStrURIENVELOPE;
	static const XMLCh * const & s_unicodeStrURIC14N_NOC;
	static const XMLCh * const & s_unicodeStrURIC14N_COM;
	static const XMLCh * const & s_unicodeStrURIEXC_C14N_NOC;
	static const XMLCh * const & s_unicodeStrURIEXC_C14N_COM;
	static const XMLCh * const & s_unicodeStrURIDSA_SHA1;
	static const XMLCh * const & s_unicodeStrURIRSA_SHA1;
	static const XMLCh * const & s_unicodeStrURIHMAC_SHA1;
	static const XMLCh * const & s_unicodeStrURIXMLNS;
	static const XMLCh * const & s_unicodeStrURIMANIFEST;

	// Internal Crypto Providers
	static const XMLCh * const & s_unicodeStrPROVOpenSSL;
	static const XMLCh * const & s_unicodeStrPROVWinCAPI;

	DSIGConstants();

	static void create();
	static void destroy();

};

inline
const XMLCh * canonicalizationMethod2UNICODEURI(canonicalizationMethod cm) {

	switch (cm) {

	case (CANON_C14N_NOC) :

		return DSIGConstants::s_unicodeStrURIC14N_NOC;
		break;

	case (CANON_C14N_COM) :

		return DSIGConstants::s_unicodeStrURIC14N_COM;
		break;

	case (CANON_C14NE_NOC) :

		return DSIGConstants::s_unicodeStrURIEXC_C14N_NOC;
		break;

	case (CANON_C14NE_COM) :

		return DSIGConstants::s_unicodeStrURIEXC_C14N_COM;
		break;

	default :
		break;

	}

	return DSIGConstants::s_unicodeStrEmpty;

}

#endif /* DSIGCONSTANTS_HEADER */

<commit_msg>(Bug: reported by Scott Cantor) - Missing break in switch<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "<WebSig>" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2001, Institute for
 * Data Communications Systems, <http://www.nue.et-inf.uni-siegen.de/>.
 * The development of this software was partly funded by the European
 * Commission in the <WebSig> project in the ISIS Programme.
 * For more information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * XSEC
 *
 * DSIGConstants := Definitions of varius DSIG constants (mainly strings)
 *
 * Author(s): Berin Lautenbach
 *
 * $Id$
 *
 */

#ifndef DSIGCONSTANTS_HEADER
#define DSIGCONSTANTS_HEADER

#include <xsec/utils/XSECSafeBuffer.hpp>

// Xerces
#include <xercesc/util/XMLString.hpp>

XSEC_USING_XERCES(XMLString);

// Name Spaces

#define URI_ID_DSIG		"http://www.w3.org/2000/09/xmldsig#"

// Hashing Algorithms

#define URI_ID_SHA1		"http://www.w3.org/2000/09/xmldsig#sha1"
#define URI_ID_MD5		"http://www.w3.org/2001/04/xmldsig-more#md5"

// Transforms

#define URI_ID_BASE64			"http://www.w3.org/2000/09/xmldsig#base64"
#define URI_ID_XPATH			"http://www.w3.org/TR/1999/REC-xpath-19991116"
#define URI_ID_XSLT				"http://www.w3.org/TR/1999/REC-xslt-19991116"
#define URI_ID_ENVELOPE			"http://www.w3.org/2000/09/xmldsig#enveloped-signature"
#define URI_ID_C14N_NOC			"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
#define URI_ID_C14N_COM			"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
#define URI_ID_EXC_C14N_NOC		"http://www.w3.org/2001/10/xml-exc-c14n#"
#define URI_ID_EXC_C14N_COM		"http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
#define XPATH_EXPR_ENVELOPE		"count(ancestor-or-self::dsig:Signature | \
								 here()/ancestor::dsig:Signature[1]) > \
								 count(ancestor-or-self::dsig:Signature)"

// Signature Algorithms

#define URI_ID_SIG_BASE		"http://www.w3.org/2000/09/xmldsig#"
#define URI_ID_SIG_DSA		"dsa"
#define URI_ID_SIG_HMAC		"hmac"
#define URI_ID_SIG_SHA1		"sha1"
#define URI_ID_SIG_RSA		"rsa"

#define URI_ID_DSA_SHA1		"http://www.w3.org/2000/09/xmldsig#dsa-sha1"
#define URI_ID_HMAC_SHA1	"http://www.w3.org/2000/09/xmldsig#hmac-sha1"
#define URI_ID_RSA_SHA1		"http://www.w3.org/2000/09/xmldsig#rsa-sha1"

// General

#define URI_ID_XMLNS	"http://www.w3.org/2000/xmlns/"
#define URI_ID_MANIFEST "http://www.w3.org/2000/09/xmldsig#Manifest"

// Internal Crypto Providers

#define PROV_OPENSSL	"OpenSSL Provider"
#define PROV_WINCAPI	"WinCAPI Provider"

// Enumerated Types

enum canonicalizationMethod {

	CANON_NONE					= 0,			// No method defined
	CANON_C14N_NOC				= 1,			// C14n without comments
	CANON_C14N_COM				= 2, 			// C14n with comments
	CANON_C14NE_NOC				= 3,			// C14n Exclusive (without comments)
	CANON_C14NE_COM				= 4				// C14n Exlusive (with Comments
};

enum signatureMethod {

	SIGNATURE_NONE				= 0,			// No method defined
	SIGNATURE_DSA				= 1, 			// DSA
	SIGNATURE_HMAC				= 2,			// Hash MAC
	SIGNATURE_RSA				= 3				// RSA
};

enum hashMethod {

	HASH_NONE					= 0,			// No method defined
	HASH_SHA1					= 1, 			// SHA1
	HASH_MD5					= 2,
};

enum transformType {

	TRANSFORM_BASE64,
	TRANSFORM_C14N,
	TRANSFORM_EXC_C14N,
	TRANSFORM_ENVELOPED_SIGNATURE,
	TRANSFORM_XPATH,
	TRANSFORM_XSLT

};

// --------------------------------------------------------------------------------
//           Some utility functions
// --------------------------------------------------------------------------------

inline
bool canonicalizationMethod2URI(safeBuffer &uri, canonicalizationMethod cm) {

	switch (cm) {

	case (CANON_C14N_NOC) :

		uri = URI_ID_C14N_NOC;
		break;

	case (CANON_C14N_COM) :

		uri = URI_ID_C14N_COM;
		break;

	case (CANON_C14NE_NOC) :

		uri = URI_ID_EXC_C14N_NOC;
		break;

	case (CANON_C14NE_COM) :

		uri = URI_ID_EXC_C14N_COM;
		break;

	default :
		return false;		// Unknown type

	}

	return true;

}

inline
bool signatureHashMethod2URI(safeBuffer &uri, signatureMethod sm, hashMethod hm) {

	uri = URI_ID_SIG_BASE;

	switch (sm) {

	case (SIGNATURE_DSA) :

		uri.sbStrcatIn(URI_ID_SIG_DSA);
		break;

	case (SIGNATURE_HMAC) :

		uri.sbStrcatIn(URI_ID_SIG_HMAC);
		break;

	case (SIGNATURE_RSA) :

		uri.sbStrcatIn(URI_ID_SIG_RSA);
		break;

	default :

		return false;

	}

	uri.sbStrcatIn("-");

	switch (hm) {

	case (HASH_SHA1) :

		uri.sbStrcatIn(URI_ID_SIG_SHA1);
		break;

	default:

		return false;

	}

	return true;

}

inline
bool hashMethod2URI(safeBuffer &uri, hashMethod hm) {

	switch (hm) {

	case (HASH_SHA1) :

		uri = URI_ID_SHA1;
		break;

	case (HASH_MD5) :

		uri = URI_ID_MD5;
		break;

	default:
		return false;

	}

	return true;

}

// --------------------------------------------------------------------------------
//           Constant Strings Class
// --------------------------------------------------------------------------------

class DSIG_EXPORT DSIGConstants {

public:

	// General strings

	static const XMLCh * const & s_unicodeStrEmpty;		// ""
	static const XMLCh * const & s_unicodeStrNL;			// "\n"
	static const XMLCh * const & s_unicodeStrXmlns;		// "xmlns"

	// DSIG Element Strings
	static const XMLCh * const & s_unicodeStrAlgorithm;

	// URI_IDs
	static const XMLCh * const & s_unicodeStrURIDSIG;
	static const XMLCh * const & s_unicodeStrURISHA1;
	static const XMLCh * const & s_unicodeStrURIMD5;		// Not recommended
	static const XMLCh * const & s_unicodeStrURIBASE64;
	static const XMLCh * const & s_unicodeStrURIXPATH;
	static const XMLCh * const & s_unicodeStrURIXSLT;
	static const XMLCh * const & s_unicodeStrURIENVELOPE;
	static const XMLCh * const & s_unicodeStrURIC14N_NOC;
	static const XMLCh * const & s_unicodeStrURIC14N_COM;
	static const XMLCh * const & s_unicodeStrURIEXC_C14N_NOC;
	static const XMLCh * const & s_unicodeStrURIEXC_C14N_COM;
	static const XMLCh * const & s_unicodeStrURIDSA_SHA1;
	static const XMLCh * const & s_unicodeStrURIRSA_SHA1;
	static const XMLCh * const & s_unicodeStrURIHMAC_SHA1;
	static const XMLCh * const & s_unicodeStrURIXMLNS;
	static const XMLCh * const & s_unicodeStrURIMANIFEST;

	// Internal Crypto Providers
	static const XMLCh * const & s_unicodeStrPROVOpenSSL;
	static const XMLCh * const & s_unicodeStrPROVWinCAPI;

	DSIGConstants();

	static void create();
	static void destroy();

};

inline
const XMLCh * canonicalizationMethod2UNICODEURI(canonicalizationMethod cm) {

	switch (cm) {

	case (CANON_C14N_NOC) :

		return DSIGConstants::s_unicodeStrURIC14N_NOC;
		break;

	case (CANON_C14N_COM) :

		return DSIGConstants::s_unicodeStrURIC14N_COM;
		break;

	case (CANON_C14NE_NOC) :

		return DSIGConstants::s_unicodeStrURIEXC_C14N_NOC;
		break;

	case (CANON_C14NE_COM) :

		return DSIGConstants::s_unicodeStrURIEXC_C14N_COM;
		break;

	default :
		break;

	}

	return DSIGConstants::s_unicodeStrEmpty;

}

#endif /* DSIGCONSTANTS_HEADER */

<|endoftext|>


<commit_before>//! @Alan
//!
//! Exercise 6.42:
//! Give the second parameter of make_plural (§ 6.3.2, p. 224) a default
//! argument of 's'. Test your program by printing singular and plural
//! versions of the words success and failure.

#include <iostream>
#include <string>

using std::string; using std::cout; using std::endl;

string make_plural(size_t ctr, const string &word, const string &ending = "s")
{
    return (ctr > 1) ? word + ending : word;
}

int main()
{
    cout<<"singual: " << make_plural(1, "success") << " "
                      << make_plural(1, "failure") << endl;
    cout<<"plural : " << make_plural(2, "success") << " "
                      << make_plural(2, "failure") << endl;
}
<commit_msg>Update ex6_42.cpp<commit_after>//! @Yue Wang
//!
//! Exercise 6.42:
//! Give the second parameter of make_plural (§ 6.3.2, p. 224) a default
//! argument of 's'. Test your program by printing singular and plural
//! versions of the words success and failure.
//!

#include <iostream>
#include <string>
using std::string; using std::cout; using std::endl;

string make_plural(size_t ctr, const string &word, const string &ending = "s")
{
    return (ctr > 1) ? word + ending : word;
}

int main()
{
    cout << "singual: " << make_plural(1, "success") << " " << make_plural(1, "failure") << endl;
    cout << "plural : " << make_plural(2, "success") << " " << make_plural(2, "failure") << endl;

    return 0;
}
<|endoftext|>


<commit_before>//
//  ex9_43.cpp
//  Exercise 9.43
//
//  Created by pezy on 6/18/15.
//  Updated by sanerror on 10/16/16.
//  Copyright (c) 2014 pezy. All rights reserved.
//
//  @Brief  Write a function that takes three strings, s, oldVal, and newVal.
//          Using iterators, and the insert and erase functions replace
//          all instances of oldVal that appear in s by newVal.
//          Test your function by using it to replace common abbreviations,
//          such as "tho" by "though" and "thru" by "through".

#include <string>
using std::string;

#include <iostream>

void Replace(string& s, const string& oldVal, const string& newVal)
{
	for (auto beg = s.begin(); beg != s.end(); ) {
		++beg;
		if (*beg != oldVal.front()) continue;
		if (std::distance(beg, s.end()) <
			std::distance(oldVal.begin(), oldVal.end()))
			break;
		if (string{ beg, beg + oldVal.size() } == oldVal) {
			beg = s.erase(beg, beg + oldVal.size());
			beg = s.insert(beg, newVal.cbegin(), newVal.cend()) + newVal.size();
		}
	}
}

int main()
{
    {
        string str{"To drive straight thru is a foolish, tho courageous act."};
        Replace(str, "thru", "through");
        Replace(str, "tho", "though");
        std::cout << str << std::endl;
    }
    {
        string str{
            "To drive straight thruthru is a foolish, thotho courageous act."};
        Replace(str, "thru", "through");
        Replace(str, "tho", "though");
        std::cout << str << std::endl;
    }
    {
        string str{"To drive straight thru is a foolish, tho courageous act."};
        Replace(str, "thru", "thruthru");
        Replace(str, "tho", "though");
        std::cout << str << std::endl;
    }
    {
        string str{"my world is a big world"};
        Replace(str, "world",
                "worldddddddddddddddddddddddddddddddddddddddddddddddd");
        std::cout << str << std::endl;
    }
    return 0;
}
<commit_msg>Update ex9_43.cpp<commit_after>//
//  ex9_43.cpp
//  Exercise 9.43
//
//  Created by pezy on 6/18/15.
//  Updated by sanerror on 10/16/16.
//  Copyright (c) 2014 pezy. All rights reserved.
//
//  @Brief  Write a function that takes three strings, s, oldVal, and newVal.
//          Using iterators, and the insert and erase functions replace
//          all instances of oldVal that appear in s by newVal.
//          Test your function by using it to replace common abbreviations,
//          such as "tho" by "though" and "thru" by "through".

#include <string>
using std::string;

#include <iostream>

void Replace(string& s, const string& oldVal, const string& newVal)
{
	for (auto beg = s.begin(); beg != s.end(); ) {
		if (std::distance(beg, s.end()) <
			std::distance(oldVal.begin(), oldVal.end()))
			break;
		if (*beg == oldVal.front() && string{ beg, beg + oldVal.size() } == oldVal) {
			beg = s.erase(beg, beg + oldVal.size());
			beg = s.insert(beg, newVal.cbegin(), newVal.cend()) + newVal.size();
		}
		else {
			++beg;
		}
	}
}
int main()
{
    {
        string str{"To drive straight thru is a foolish, tho courageous act."};
        Replace(str, "thru", "through");
        Replace(str, "tho", "though");
        std::cout << str << std::endl;
    }
    {
        string str{
            "To drive straight thruthru is a foolish, thotho courageous act."};
        Replace(str, "thru", "through");
        Replace(str, "tho", "though");
        std::cout << str << std::endl;
    }
    {
        string str{"To drive straight thru is a foolish, tho courageous act."};
        Replace(str, "thru", "thruthru");
        Replace(str, "tho", "though");
        std::cout << str << std::endl;
    }
    {
        string str{"my world is a big world"};
        Replace(str, "world",
                "worldddddddddddddddddddddddddddddddddddddddddddddddd");
        std::cout << str << std::endl;
    }
    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the COPYING file.
 */

/** @file qi/log.hpp
 *  @brief Convenient log macro
 */

#pragma once
#ifndef _LIBQI_QI_LOG_HPP_
#define _LIBQI_QI_LOG_HPP_

# include <map>
# include <string>
# include <iostream>
# include <sstream>
# include <cstdarg>
# include <cstdio>

#include <boost/function/function_fwd.hpp>

#include <qi/config.hpp>
#include <qi/os.hpp>

#if defined(NO_QI_DEBUG) || defined(NDEBUG)
# define qiLogDebug(...)        if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
#   define qiLogDebug(...)      qi::log::LogStream(qi::log::debug, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
#   define qiLogDebug(...)      qi::log::LogStream(qi::log::debug, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_VERBOSE
# define qiLogVerbose(...)      if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogVerbose(...)      qi::log::LogStream(qi::log::verbose, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogVerbose(...)      qi::log::LogStream(qi::log::verbose, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_INFO
# define qiLogInfo(...)         if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogInfo(...)         qi::log::LogStream(qi::log::info, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogInfo(...)         qi::log::LogStream(qi::log::info, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_WARNING
# define qiLogWarning(...)      if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogWarning(...)      qi::log::LogStream(qi::log::warning, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogWarning(...)      qi::log::LogStream(qi::log::warning, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_ERROR
# define qiLogError(...)        if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogError(...)        qi::log::LogStream(qi::log::error, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogError(...)        qi::log::LogStream(qi::log::error, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_FATAL
# define qiLogFatal(...)        if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogFatal(...)        qi::log::LogStream(qi::log::fatal, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogFatal(...)        qi::log::LogStream(qi::log::fatal, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

// enum level {
//   silent = 0,
//   fatal,
//   error,
//   warning,
//   info,
//   verbose,
//   debug
// };

namespace qi {
  namespace log {

    namespace detail {

      class NullStream {
      public:
        NullStream(const char *, ...)
        {
        }

        NullStream &self()
        {
          return *this;
        }

        template <typename T>
        NullStream& operator<<(const T &QI_UNUSED(val))
        {
          return self();
        }

        NullStream& operator<<(std::ostream& (*QI_UNUSED(f))(std::ostream&))
        {
          return self();
        }

      };

    };

    enum LogLevel {
        silent = 0,
        fatal,
        error,
        warning,
        info,
        verbose,
        debug
    };

    typedef boost::function7<void,
                             const qi::log::LogLevel,
                             const qi::os::timeval,
                             const char*,
                             const char*,
                             const char*,
                             const char*,
                             int> logFuncHandler;

    QI_API void init(qi::log::LogLevel verb = qi::log::info,
                     int ctx = 0,
                     bool synchronous = true);

    QI_API void destroy();

    QI_API void log(const qi::log::LogLevel verb,
                    const char              *category,
                    const char              *msg,
                    const char              *file = "",
                    const char              *fct = "",
                    const int               line = 0);

    QI_API const char* logLevelToString(const qi::log::LogLevel verb);

    QI_API qi::log::LogLevel stringToLogLevel(const char* verb);

    QI_API void setVerbosity(const qi::log::LogLevel lv);

    QI_API qi::log::LogLevel verbosity();

    QI_API void setContext(int ctx);

    QI_API int context();

    QI_API void setSynchronousLog(bool sync);

    QI_API void addLogHandler(const std::string& name,
                              qi::log::logFuncHandler fct);

    QI_API void removeLogHandler(const std::string& name);

    QI_API void flush();

    class LogStream: public std::stringstream
    {
    public:
      LogStream(const LogLevel    level,
                const char        *file,
                const char        *function,
                const int         line,
                const char        *category)
        : _logLevel(level)
        , _category(category)
        , _file(file)
        , _function(function)
        , _line(line)
      {
      }

      LogStream(const LogLevel    level,
                const char        *file,
                const char        *function,
                const int         line,
                const char        *category,
                const char        *fmt, ...)
        : _logLevel(level)
        , _category(category)
        , _file(file)
        , _function(function)
        , _line(line)
      {
        char buffer[2048];
        va_list vl;
        va_start(vl, fmt);
       #ifdef _MSC_VER
        vsnprintf_s(buffer, 2048, 2047, fmt, vl);
       #else
        vsnprintf(buffer, 2048, fmt, vl);
       #endif
        buffer[2047] = 0;
        va_end(vl);
        *this << buffer;
      }

      ~LogStream()
      {
        qi::log::log(_logLevel, _category, this->str().c_str(), _file, _function, _line);
      }

      LogStream& self() {
        return *this;
      }

    private:
      LogLevel    _logLevel;
      const char *_category;
      const char *_file;
      const char *_function;
      int         _line;

      //avoid copy
      LogStream(const LogStream &rhs);
      LogStream &operator=(const LogStream &rhs);
    };
  }
}

#endif  // _LIBQI_QI_LOG_HPP_
<commit_msg>Remove some redundant extra qualification<commit_after>/*
 * Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the COPYING file.
 */

/** @file qi/log.hpp
 *  @brief Convenient log macro
 */

#pragma once
#ifndef _LIBQI_QI_LOG_HPP_
#define _LIBQI_QI_LOG_HPP_

# include <map>
# include <string>
# include <iostream>
# include <sstream>
# include <cstdarg>
# include <cstdio>

#include <boost/function/function_fwd.hpp>

#include <qi/config.hpp>
#include <qi/os.hpp>

#if defined(NO_QI_DEBUG) || defined(NDEBUG)
# define qiLogDebug(...)        if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
#   define qiLogDebug(...)      qi::log::LogStream(qi::log::debug, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
#   define qiLogDebug(...)      qi::log::LogStream(qi::log::debug, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_VERBOSE
# define qiLogVerbose(...)      if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogVerbose(...)      qi::log::LogStream(qi::log::verbose, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogVerbose(...)      qi::log::LogStream(qi::log::verbose, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_INFO
# define qiLogInfo(...)         if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogInfo(...)         qi::log::LogStream(qi::log::info, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogInfo(...)         qi::log::LogStream(qi::log::info, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_WARNING
# define qiLogWarning(...)      if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogWarning(...)      qi::log::LogStream(qi::log::warning, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogWarning(...)      qi::log::LogStream(qi::log::warning, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_ERROR
# define qiLogError(...)        if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogError(...)        qi::log::LogStream(qi::log::error, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogError(...)        qi::log::LogStream(qi::log::error, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

#ifdef NO_QI_FATAL
# define qiLogFatal(...)        if (false) qi::log::detail::NullStream(__VA_ARGS__).self()
#elif defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
# define qiLogFatal(...)        qi::log::LogStream(qi::log::fatal, "", __FUNCTION__, 0, __VA_ARGS__).self()
#else
# define qiLogFatal(...)        qi::log::LogStream(qi::log::fatal, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
#endif

// enum level {
//   silent = 0,
//   fatal,
//   error,
//   warning,
//   info,
//   verbose,
//   debug
// };

namespace qi {
  namespace log {

    namespace detail {

      class NullStream {
      public:
        NullStream(const char *, ...)
        {
        }

        NullStream &self()
        {
          return *this;
        }

        template <typename T>
        NullStream& operator<<(const T &QI_UNUSED(val))
        {
          return self();
        }

        NullStream& operator<<(std::ostream& (*QI_UNUSED(f))(std::ostream&))
        {
          return self();
        }

      };

    };

    enum LogLevel {
        silent = 0,
        fatal,
        error,
        warning,
        info,
        verbose,
        debug
    };

    typedef boost::function7<void,
                             const qi::log::LogLevel,
                             const qi::os::timeval,
                             const char*,
                             const char*,
                             const char*,
                             const char*,
                             int> logFuncHandler;

    QI_API void init(qi::log::LogLevel verb = qi::log::info,
                     int ctx = 0,
                     bool synchronous = true);

    QI_API void destroy();

    QI_API void log(const qi::log::LogLevel verb,
                    const char              *category,
                    const char              *msg,
                    const char              *file = "",
                    const char              *fct = "",
                    const int               line = 0);

    QI_API const char* logLevelToString(const qi::log::LogLevel verb);

    QI_API LogLevel stringToLogLevel(const char* verb);

    QI_API void setVerbosity(const qi::log::LogLevel lv);

    QI_API LogLevel verbosity();

    QI_API void setContext(int ctx);

    QI_API int context();

    QI_API void setSynchronousLog(bool sync);

    QI_API void addLogHandler(const std::string& name,
                              qi::log::logFuncHandler fct);

    QI_API void removeLogHandler(const std::string& name);

    QI_API void flush();

    class LogStream: public std::stringstream
    {
    public:
      LogStream(const LogLevel    level,
                const char        *file,
                const char        *function,
                const int         line,
                const char        *category)
        : _logLevel(level)
        , _category(category)
        , _file(file)
        , _function(function)
        , _line(line)
      {
      }

      LogStream(const LogLevel    level,
                const char        *file,
                const char        *function,
                const int         line,
                const char        *category,
                const char        *fmt, ...)
        : _logLevel(level)
        , _category(category)
        , _file(file)
        , _function(function)
        , _line(line)
      {
        char buffer[2048];
        va_list vl;
        va_start(vl, fmt);
       #ifdef _MSC_VER
        vsnprintf_s(buffer, 2048, 2047, fmt, vl);
       #else
        vsnprintf(buffer, 2048, fmt, vl);
       #endif
        buffer[2047] = 0;
        va_end(vl);
        *this << buffer;
      }

      ~LogStream()
      {
        qi::log::log(_logLevel, _category, this->str().c_str(), _file, _function, _line);
      }

      LogStream& self() {
        return *this;
      }

    private:
      LogLevel    _logLevel;
      const char *_category;
      const char *_file;
      const char *_function;
      int         _line;

      //avoid copy
      LogStream(const LogStream &rhs);
      LogStream &operator=(const LogStream &rhs);
    };
  }
}

#endif  // _LIBQI_QI_LOG_HPP_
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "classad/common.h"
#include "classad/classad.h"

using namespace std;

BEGIN_NAMESPACE( classad )

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, const string &attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = attrname;
	expr = tree;
	absolute = absolut;
}

AttributeReference::
AttributeReference(const AttributeReference &ref)
{
    CopyFrom(ref);
    return;
}

AttributeReference::
~AttributeReference()
{
	if( expr ) delete expr;
}

AttributeReference &AttributeReference::
operator=(const AttributeReference &ref)
{
    if (this != &ref) {
        CopyFrom(ref);
    }
    return *this;
}

ExprTree *AttributeReference::
Copy( ) const
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) {
		CondorErrno = ERR_MEM_ALLOC_FAILED;
		CondorErrMsg = "";
		return NULL;
	}

    if (!newTree->CopyFrom(*this)) {
        delete newTree;
        newTree = NULL;
    }

	return newTree;
}

bool AttributeReference::
CopyFrom(const AttributeReference &ref)
{
    bool success;

    success = true;

	attributeStr = ref.attributeStr;
	if( ref.expr && ( expr=ref.expr->Copy( ) ) == NULL ) {
        success = false;
	} else {
        ExprTree::CopyFrom(ref);
        absolute = ref.absolute;
    }
    return success;
}

bool AttributeReference::
SameAs(const ExprTree *tree) const
{
    bool is_same;

    if (tree->GetKind() != ATTRREF_NODE) {
        is_same = false;
    } else {
        AttributeReference *other_ref = (AttributeReference *) tree;

        if (   absolute     != other_ref->absolute
            || attributeStr != other_ref->attributeStr) {
            is_same = false;
        } else if (    (expr == NULL && other_ref->expr == NULL)
                    || (expr == other_ref->expr)
                    || (expr != NULL && other_ref->expr != NULL && expr->SameAs(other_ref->expr))) {
            // Will this check result in infinite recursion? How do I stop it?
            is_same = true;
        } else {
            is_same = false;
        }
    }

    return is_same;
}

bool
operator==(const AttributeReference &ref1, const AttributeReference &ref2)
{
    return ref1.SameAs(&ref2);
}

void AttributeReference::
_SetParentScope( const ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

void AttributeReference::
GetComponents( ExprTree *&tree, string &attr, bool &abs ) const
{
	tree = expr;
	attr = attributeStr;
	abs = absolute;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;
	EvalCache::iterator	itr;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			// lookup in cache
			itr = state.cache.find( tree );

			// if found, return cached value
			if( itr != state.cache.end( ) ) {
				val.CopyFrom( itr->second );
				state.curAd = curAd;		// NAC (fixed a bug)
				return true;
			}

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Evaluate( state , val );

			// replace cached undef with actual evaluation
			state.cache[ tree ] = val;

			state.curAd = curAd;

			return rval;

		default:  CLASSAD_EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig ) const
{
	ExprTree	*tree;
	ExprTree	*exprSig;
	ClassAd		*curAd;
	bool		rval;
	Value 		cv;
	EvalCache::iterator	itr;

	curAd = state.curAd;
	exprSig = NULL;
	rval 	= true;

	switch( FindExpr( state , tree , exprSig , true ) ) {
		case EVAL_FAIL:
			rval = false;
			break;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue( );
			break;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue( );
			break;

		case EVAL_OK:

			// lookup in cache
			itr = state.cache.find( tree );

			// if found in cache, return cached value
			if( itr != state.cache.end( ) ) {
				val.CopyFrom( itr->second );
				state.curAd = curAd;		// NAC (fixed a bug)
				return true;
			}

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Evaluate( state, val );

			// replace undef in cache with actual evaluation
			state.cache[ tree ] = val;

			break;

		default:  CLASSAD_EXCEPT( "ClassAd:  Should not reach here" );
	}
	if(!rval || !(sig=new AttributeReference(exprSig,attributeStr,absolute))){
		if( rval ) {
			CondorErrno = ERR_MEM_ALLOC_FAILED;
			CondorErrMsg = "";
		}
		delete exprSig;
		sig = NULL;
		return( false );
	}
	state.curAd = curAd;
	return rval;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, int*) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;
	EvalCache::iterator	itr;

	ntree = NULL; // Just to be safe...  wenger 2003-12-11.

		// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			if( !(ntree = Copy( ) ) ) {
				CondorErrno = ERR_MEM_ALLOC_FAILED;
				CondorErrMsg = "";
				return( false );
			}
			state.curAd = curAd;
			return true;

		case EVAL_OK:

			// lookup in cache
			itr = state.cache.find( tree );

			// if found in cache, return cached value
			if( itr != state.cache.end( ) ) {
				val.CopyFrom( itr->second );
				if( val.IsUndefinedValue( ) ) {
					if( !(ntree = Copy( ) ) ) {
						CondorErrno = ERR_MEM_ALLOC_FAILED;
						CondorErrMsg = "";
						return( false );
					}
				} else {
						// Fixes the bug I (wenger) ran into with Flatten()
						// returning an expression with a bad pointer.
					ntree = NULL;
				}
				state.curAd = curAd;
				return true;
			}

			// temporarily cache value as undef, so any circular refs
			// in Flatten() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Flatten( state, val, ntree );

			// don't inline if it didn't flatten to a value, and clear cache
			// do inline if FlattenAndInline was called
			if( ntree ) {
				if( state.flattenAndInline ) {	// NAC
					return true;   				// NAC
				}								// NAC
				delete ntree;
				ntree = Copy( );
				val.SetUndefinedValue( );
				state.cache.erase( tree );
			} else {
				state.cache[ tree ] = val;
			}

			state.curAd = curAd;
			return rval;

		default:  CLASSAD_EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

int AttributeReference::
FindExpr(EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig) const
{
	ClassAd	*current=NULL;
	const ExprList *adList = NULL;
	Value	val;
	bool	rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
		if( absolute && ( current == NULL ) ) {	// NAC - circularity so no root
			return EVAL_FAIL;					// NAC
		}										// NAC
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sig):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			return( PROP_UNDEF );
		} else if( val.IsErrorValue( ) ) {
			return( PROP_ERROR );
		}

		if( !val.IsClassAdValue( current ) && !val.IsListValue( adList ) ) {
			return( EVAL_ERROR );
		}
	}

	if( val.IsListValue( ) ) {
		vector< ExprTree *> eVector;
		const ExprTree *currExpr;
			// iterate through exprList and apply attribute reference
			// to each exprTree
		for(ExprListIterator itr(adList);!itr.IsAfterLast( );itr.NextExpr( )){
 			currExpr = itr.CurrentExpr( );
			if( currExpr == NULL ) {
				return( EVAL_FAIL );
			} else {
				AttributeReference *attrRef = NULL;
				attrRef = MakeAttributeReference( currExpr->Copy( ),
												  attributeStr,
												  false );
				val.Clear( );
					// Create new EvalState, within this scope, because
					// attrRef is only temporary, so we do not want to
					// cache the evaluated result in the outer state object.
				EvalState tstate;
				tstate.SetScopes(state.curAd);
				rval = wantSig ? attrRef->Evaluate( tstate, val, sig )
					: attrRef->Evaluate( tstate, val );
				delete attrRef;
				if( !rval ) {
					return( EVAL_FAIL );
				}

				ClassAd *evaledAd = NULL;
				const ExprList *evaledList = NULL;
				if( val.IsClassAdValue( evaledAd ) ) {
					eVector.push_back( evaledAd );
					continue;
				}
				else if( val.IsListValue( evaledList ) ) {
					eVector.push_back( evaledList->Copy( ) );
					continue;
				}
				else {
					eVector.push_back( Literal::MakeLiteral( val ) );
				}
			}
		}
		tree = ExprList::MakeExprList( eVector );
		ClassAd *newRoot = new ClassAd( );
		((ExprList*)tree)->SetParentScope( newRoot );
		return EVAL_OK;
	}
		// lookup with scope; this may side-affect state
	return( current->LookupInScope( attributeStr, tree, state ) );
}

AttributeReference *AttributeReference::
MakeAttributeReference(ExprTree *tree, const string &attrStr, bool absolut)
{
	return( new AttributeReference( tree, attrStr, absolut ) );
}

END_NAMESPACE // classad
<commit_msg>Using single-lookup insert method in attrref EvalCache insertion. #990<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "classad/common.h"
#include "classad/classad.h"

using namespace std;

BEGIN_NAMESPACE( classad )

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, const string &attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = attrname;
	expr = tree;
	absolute = absolut;
}

AttributeReference::
AttributeReference(const AttributeReference &ref)
{
    CopyFrom(ref);
    return;
}

AttributeReference::
~AttributeReference()
{
	if( expr ) delete expr;
}

AttributeReference &AttributeReference::
operator=(const AttributeReference &ref)
{
    if (this != &ref) {
        CopyFrom(ref);
    }
    return *this;
}

ExprTree *AttributeReference::
Copy( ) const
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) {
		CondorErrno = ERR_MEM_ALLOC_FAILED;
		CondorErrMsg = "";
		return NULL;
	}

    if (!newTree->CopyFrom(*this)) {
        delete newTree;
        newTree = NULL;
    }

	return newTree;
}

bool AttributeReference::
CopyFrom(const AttributeReference &ref)
{
    bool success;

    success = true;

	attributeStr = ref.attributeStr;
	if( ref.expr && ( expr=ref.expr->Copy( ) ) == NULL ) {
        success = false;
	} else {
        ExprTree::CopyFrom(ref);
        absolute = ref.absolute;
    }
    return success;
}

bool AttributeReference::
SameAs(const ExprTree *tree) const
{
    bool is_same;

    if (tree->GetKind() != ATTRREF_NODE) {
        is_same = false;
    } else {
        AttributeReference *other_ref = (AttributeReference *) tree;

        if (   absolute     != other_ref->absolute
            || attributeStr != other_ref->attributeStr) {
            is_same = false;
        } else if (    (expr == NULL && other_ref->expr == NULL)
                    || (expr == other_ref->expr)
                    || (expr != NULL && other_ref->expr != NULL && expr->SameAs(other_ref->expr))) {
            // Will this check result in infinite recursion? How do I stop it?
            is_same = true;
        } else {
            is_same = false;
        }
    }

    return is_same;
}

bool
operator==(const AttributeReference &ref1, const AttributeReference &ref2)
{
    return ref1.SameAs(&ref2);
}

void AttributeReference::
_SetParentScope( const ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

void AttributeReference::
GetComponents( ExprTree *&tree, string &attr, bool &abs ) const
{
	tree = expr;
	attr = attributeStr;
	abs = absolute;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
		{
				// insert UNDEFINED in cache so circular references
				// will not loop
			pair<EvalCache::iterator,bool> insert_result =
				state.cache.insert( EvalCache::value_type(tree,cv) );

			if( !insert_result.second ) {
					// already in cache
				val.CopyFrom( insert_result.first->second );
				state.curAd = curAd;
				return true;
			}

			rval = tree->Evaluate( state, val );

			// replace cached undef with actual evaluation
			insert_result.first->second = val;

			state.curAd = curAd;

			return rval;
		}
		default:  CLASSAD_EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig ) const
{
	ExprTree	*tree;
	ExprTree	*exprSig;
	ClassAd		*curAd;
	bool		rval;
	Value 		cv;

	curAd = state.curAd;
	exprSig = NULL;
	rval 	= true;

	switch( FindExpr( state , tree , exprSig , true ) ) {
		case EVAL_FAIL:
			rval = false;
			break;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue( );
			break;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue( );
			break;

		case EVAL_OK:
		{
				// insert UNDEFINED in cache so circular references
				// will not loop
			pair<EvalCache::iterator,bool> insert_result =
				state.cache.insert( EvalCache::value_type(tree,cv) );

			if( !insert_result.second ) {
					// already in cache
				val.CopyFrom( insert_result.first->second );
				state.curAd = curAd;
				return true;
			}

			rval = tree->Evaluate( state, val );

			// replace cached undef with actual evaluation
			insert_result.first->second = val;

			break;
		}
		default:  CLASSAD_EXCEPT( "ClassAd:  Should not reach here" );
	}
	if(!rval || !(sig=new AttributeReference(exprSig,attributeStr,absolute))){
		if( rval ) {
			CondorErrno = ERR_MEM_ALLOC_FAILED;
			CondorErrMsg = "";
		}
		delete exprSig;
		sig = NULL;
		return( false );
	}
	state.curAd = curAd;
	return rval;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, int*) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;
	EvalCache::iterator	itr;

	ntree = NULL; // Just to be safe...  wenger 2003-12-11.

		// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			if( !(ntree = Copy( ) ) ) {
				CondorErrno = ERR_MEM_ALLOC_FAILED;
				CondorErrMsg = "";
				return( false );
			}
			state.curAd = curAd;
			return true;

		case EVAL_OK:
		{
				// insert UNDEFINED in cache so circular references
				// will not loop
			pair<EvalCache::iterator,bool> insert_result =
				state.cache.insert( EvalCache::value_type(tree,cv) );

			if( !insert_result.second ) {
					// already in cache
				val.CopyFrom( insert_result.first->second );
				if( val.IsUndefinedValue( ) ) {
					if( !(ntree = Copy( ) ) ) {
						CondorErrno = ERR_MEM_ALLOC_FAILED;
						CondorErrMsg = "";
						return( false );
					}
				} else {
						// Fixes the bug I (wenger) ran into with Flatten()
						// returning an expression with a bad pointer.
					ntree = NULL;
				}
				state.curAd = curAd;
				return true;
			}

			rval = tree->Flatten( state, val, ntree );

			// don't inline if it didn't flatten to a value, and clear cache
			// do inline if FlattenAndInline was called
			if( ntree ) {
				if( state.flattenAndInline ) {	// NAC
					return true;   				// NAC
				}								// NAC
				delete ntree;
				ntree = Copy( );
				val.SetUndefinedValue( );
				state.cache.erase( tree );
			} else {
				insert_result.first->second = val;
			}

			state.curAd = curAd;
			return rval;
		}
		default:  CLASSAD_EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

int AttributeReference::
FindExpr(EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig) const
{
	ClassAd	*current=NULL;
	const ExprList *adList = NULL;
	Value	val;
	bool	rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
		if( absolute && ( current == NULL ) ) {	// NAC - circularity so no root
			return EVAL_FAIL;					// NAC
		}										// NAC
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sig):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			return( PROP_UNDEF );
		} else if( val.IsErrorValue( ) ) {
			return( PROP_ERROR );
		}

		if( !val.IsClassAdValue( current ) && !val.IsListValue( adList ) ) {
			return( EVAL_ERROR );
		}
	}

	if( val.IsListValue( ) ) {
		vector< ExprTree *> eVector;
		const ExprTree *currExpr;
			// iterate through exprList and apply attribute reference
			// to each exprTree
		for(ExprListIterator itr(adList);!itr.IsAfterLast( );itr.NextExpr( )){
 			currExpr = itr.CurrentExpr( );
			if( currExpr == NULL ) {
				return( EVAL_FAIL );
			} else {
				AttributeReference *attrRef = NULL;
				attrRef = MakeAttributeReference( currExpr->Copy( ),
												  attributeStr,
												  false );
				val.Clear( );
					// Create new EvalState, within this scope, because
					// attrRef is only temporary, so we do not want to
					// cache the evaluated result in the outer state object.
				EvalState tstate;
				tstate.SetScopes(state.curAd);
				rval = wantSig ? attrRef->Evaluate( tstate, val, sig )
					: attrRef->Evaluate( tstate, val );
				delete attrRef;
				if( !rval ) {
					return( EVAL_FAIL );
				}

				ClassAd *evaledAd = NULL;
				const ExprList *evaledList = NULL;
				if( val.IsClassAdValue( evaledAd ) ) {
					eVector.push_back( evaledAd );
					continue;
				}
				else if( val.IsListValue( evaledList ) ) {
					eVector.push_back( evaledList->Copy( ) );
					continue;
				}
				else {
					eVector.push_back( Literal::MakeLiteral( val ) );
				}
			}
		}
		tree = ExprList::MakeExprList( eVector );
		ClassAd *newRoot = new ClassAd( );
		((ExprList*)tree)->SetParentScope( newRoot );
		return EVAL_OK;
	}
		// lookup with scope; this may side-affect state
	return( current->LookupInScope( attributeStr, tree, state ) );
}

AttributeReference *AttributeReference::
MakeAttributeReference(ExprTree *tree, const string &attrStr, bool absolut)
{
	return( new AttributeReference( tree, attrStr, absolut ) );
}

END_NAMESPACE // classad
<|endoftext|>


<commit_before>#include "text_blob.h"
#include "wx/graphics.h"
#include "wx/osx/core/cfstring.h"

#import <CoreText/CoreText.h>

#include <iostream>
#include <algorithm>
#include <vector>

#include "char_width.h"

static
void __DoDrawText(CGContextRef cgContext, const wxTextBlob::TextPart & text_part, double adv_x);

void wxTextBlob::DoDrawBackground(wxGraphicsContext * context, void * rendering_data)
{
    const BackgroundRectVector & bg_rect_vector = *(BackgroundRectVector*)rendering_data;

    for(const auto & it : bg_rect_vector) {
        context->SetBrush(context->CreateBrush(wxBrush(it.back)));
        context->DrawRectangle(it.rect.GetX(), it.rect.GetY(), it.rect.GetWidth(), it.rect.GetHeight());
    }
}

void wxTextBlob::DoDrawText(wxGraphicsContext * context, void * rendering_data)
{
    (void)rendering_data;
    CGContextRef native_context = (CGContextRef)context->GetNativeContext();

    for(auto it = m_TextParts.begin(),
                it_end = m_TextParts.end();
        it != it_end;
        it++) {
        __DoDrawText(native_context, *it, m_GlyphAdvanceX);
    }
}

void * wxTextBlob::BeginTextRendering() {
    BackgroundRectVector * rdata = new BackgroundRectVector;

    BackgroundRectVector & bg_rect_vector = *rdata;

    for(auto it = m_TextParts.begin(),
                it_end = m_TextParts.end();
        it != it_end;
        it++) {

        if (it->back != wxNullColour) {
            bg_rect_vector.push_back({{it->pt, it->size}, it->back});
        }
    }

    return rdata;
}

void wxTextBlob::EndTextRendering(void * rendering_data) {
    BackgroundRectVector * rdata = (BackgroundRectVector *)rendering_data;

    delete rdata;
}

static
void __DrawLine(const wxString & str, CTLineRef line, CGContextRef cgContext, double adv_x);

static
void __DoDrawText(CGContextRef cgContext, const wxTextBlob::TextPart & text_part, double adv_x)
{
    wxCFStringRef text(text_part.text, wxLocale::GetSystemEncoding() );
    CTFontRef font = text_part.pFont->OSXGetCTFont();
    CGColorRef col = text_part.fore.CreateCGColor();

    CFStringRef keys[] = { kCTFontAttributeName , kCTForegroundColorAttributeName};
    CFTypeRef values[] = { font, col};

    wxCFRef<CFDictionaryRef> attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values,
                                                    WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) );
    wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, attributes) );
    wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) );

    CGFloat x = (CGFloat)text_part.pt.x;
    CGFloat y = (CGFloat)text_part.pt.y;

    y += CTFontGetAscent(font);

    CGContextSaveGState(cgContext);
    CGAffineTransform textMatrix = CGContextGetTextMatrix(cgContext);

    CGContextTranslateCTM(cgContext, (CGFloat)x, (CGFloat)y);
    CGContextScaleCTM(cgContext, 1, -1);
    CGContextSetTextMatrix(cgContext, CGAffineTransformIdentity);

    if (!adv_x)
        CTLineDraw(line, cgContext);
    else
        __DrawLine(text_part.text, line, cgContext, adv_x);

    if ( text_part.pFont->GetUnderlined() ) {
        //AKT: draw horizontal line 1 pixel thick and with 1 pixel gap under baseline
        CGFloat width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);

        CGPoint points[] = { {0.0, -2.0},  {width, -2.0} };

        CGContextSetStrokeColorWithColor(cgContext, col);
        CGContextSetShouldAntialias(cgContext, false);
        CGContextSetLineWidth(cgContext, 1.0);
        CGContextStrokeLineSegments(cgContext, points, 2);
    }
    if ( text_part.pFont->GetStrikethrough() )
    {
        CGFloat width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
        CGFloat height = CTFontGetXHeight( font );
        CGPoint points[] = { {0.0, height * 0.6},  {width, height * 0.6} };
        CGContextSetStrokeColorWithColor(cgContext, col);
        CGContextSetShouldAntialias(cgContext, false);
        CGContextSetLineWidth(cgContext, 1.0);
        CGContextStrokeLineSegments(cgContext, points, 2);
    }

    CGContextRestoreGState(cgContext);
    CGContextSetTextMatrix(cgContext, textMatrix);
    CGColorRelease( col );
}

static
void __ApplyStyles(CTRunRef run, CGContextRef context) {
    CFDictionaryRef attributes = CTRunGetAttributes(run);

    // Set the font
    CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(attributes, kCTFontAttributeName);
    CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL);
    CGContextSetFont(context, cgFont);
    CGContextSetFontSize(context, CTFontGetSize(runFont));
    CFRelease(cgFont);

    // Set the color
    CGColorRef color = (CGColorRef)CFDictionaryGetValue(attributes, kCTForegroundColorAttributeName);
    CGContextSetFillColorWithColor(context, color);
}

static
void __DrawRun(const wxString & text, CTRunRef run, CGContextRef cgContext, double adv_x) {
    __ApplyStyles(run, cgContext);

    CFRange range = CTRunGetStringRange(run);
    wxWCharBuffer buf = text.wc_str();

    std::vector<CGPoint> positions;
    std::vector<CGGlyph> glyphs;

    CFDictionaryRef attributes = CTRunGetAttributes(run);
    CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(attributes, kCTFontAttributeName);

    CGPoint pt = CGPointMake(0, 0);

    for(size_t i = 0; i < buf.length(); i++) {
        wchar_t ch = buf[(size_t)i];

        if ((CFIndex)i < range.location) {
            pt.x += (char_width(ch) > 1 ? adv_x * 2 : adv_x);
            continue;
        }

        if ((CFIndex)i >= range.location + range.length)
            break;

        CGGlyph glyph;
        CTFontGetGlyphsForCharacters(runFont, (UniChar*)&ch, &glyph, 1);

        positions.push_back(pt);
        glyphs.push_back(glyph);

        pt.x += (char_width(ch) > 1 ? adv_x * 2 : adv_x);
    }

    if (glyphs.size() > 0) {
        CGContextShowGlyphsAtPositions(cgContext, &glyphs[0], &positions[0], glyphs.size());
    }
}

static
void __DrawLine(const wxString & text, CTLineRef line, CGContextRef cgContext, double adv_x) {
    (void)line;
    (void)cgContext;
    (void)adv_x;

    CFArrayRef runs = CTLineGetGlyphRuns(line);

    for(CFIndex i=0;i < CFArrayGetCount(runs); i++) {
        CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i);

        __DrawRun(text, run, cgContext, adv_x);
    }
}
<commit_msg>add test code for using CTRunDelegate<commit_after>#include "text_blob.h"
#include "wx/graphics.h"
#include "wx/osx/core/cfstring.h"

#import <CoreText/CoreText.h>

#include <iostream>
#include <algorithm>
#include <vector>

#include "char_width.h"

#define USE_DELEGATE 0

#if USE_DELEGATE
typedef struct __DelegateCallbackData {
    CGFloat ascent;
    CGFloat descent;
    CGFloat adv_x;
} DelegateCallbackData;

/* Callbacks */
static
void MyDeallocationCallback( void* refCon ){
    (void)refCon;
}

static
CGFloat MyGetAscentCallback( void *refCon ){
    DelegateCallbackData * data = (DelegateCallbackData*)refCon;
    return data->ascent;
}

static
CGFloat MyGetDescentCallback( void *refCon ){
    DelegateCallbackData * data = (DelegateCallbackData*)refCon;
    return data->descent;
}

static
CGFloat MyGetWidthCallback( void* refCon ){
    DelegateCallbackData * data = (DelegateCallbackData*)refCon;
    return data->adv_x;
}
#endif

static
void __DoDrawText(CGContextRef cgContext, const wxTextBlob::TextPart & text_part, double adv_x);

void wxTextBlob::DoDrawBackground(wxGraphicsContext * context, void * rendering_data)
{
    const BackgroundRectVector & bg_rect_vector = *(BackgroundRectVector*)rendering_data;

    for(const auto & it : bg_rect_vector) {
        context->SetBrush(context->CreateBrush(wxBrush(it.back)));
        context->DrawRectangle(it.rect.GetX(), it.rect.GetY(), it.rect.GetWidth(), it.rect.GetHeight());
    }
}

void wxTextBlob::DoDrawText(wxGraphicsContext * context, void * rendering_data)
{
    (void)rendering_data;
    CGContextRef native_context = (CGContextRef)context->GetNativeContext();

    for(auto it = m_TextParts.begin(),
                it_end = m_TextParts.end();
        it != it_end;
        it++) {
        __DoDrawText(native_context, *it, m_GlyphAdvanceX);
    }
}

void * wxTextBlob::BeginTextRendering() {
    BackgroundRectVector * rdata = new BackgroundRectVector;

    BackgroundRectVector & bg_rect_vector = *rdata;

    for(auto it = m_TextParts.begin(),
                it_end = m_TextParts.end();
        it != it_end;
        it++) {

        if (it->back != wxNullColour) {
            bg_rect_vector.push_back({{it->pt, it->size}, it->back});
        }
    }

    return rdata;
}

void wxTextBlob::EndTextRendering(void * rendering_data) {
    BackgroundRectVector * rdata = (BackgroundRectVector *)rendering_data;

    delete rdata;
}

#if !USE_DELEGATE
static
void __DrawLine(const wxString & str, CTLineRef line, CGContextRef cgContext, double adv_x);
#endif

static
void __DoDrawText(CGContextRef cgContext, const wxTextBlob::TextPart & text_part, double adv_x)
{
    wxCFStringRef text(text_part.text, wxLocale::GetSystemEncoding() );
    CTFontRef font = text_part.pFont->OSXGetCTFont();
    CGColorRef col = text_part.fore.CreateCGColor();

#if USE_DELEGATE
    DelegateCallbackData data {
        CTFontGetAscent(font),
        CTFontGetDescent(font),
        adv_x
    };

    CTRunDelegateCallbacks callbacks;
    callbacks.version = kCTRunDelegateVersion1;
    callbacks.dealloc = MyDeallocationCallback;
    callbacks.getAscent = MyGetAscentCallback;
    callbacks.getDescent = MyGetDescentCallback;
    callbacks.getWidth = MyGetWidthCallback;
    CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, &data);
#endif

    CFStringRef keys[] = { kCTFontAttributeName , kCTForegroundColorAttributeName
#if USE_DELEGATE
                           , kCTRunDelegateAttributeName
#endif
    };

    CFTypeRef values[] = { font, col
#if USE_DELEGATE
                           , delegate
#endif
    };

    wxCFRef<CFDictionaryRef> attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values,
                                                            WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) );
    wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, attributes) );
    wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) );

    CGFloat x = (CGFloat)text_part.pt.x;
    CGFloat y = (CGFloat)text_part.pt.y;

    y += CTFontGetAscent(font);

    CGContextSaveGState(cgContext);
    CGAffineTransform textMatrix = CGContextGetTextMatrix(cgContext);

    CGContextTranslateCTM(cgContext, (CGFloat)x, (CGFloat)y);
    CGContextScaleCTM(cgContext, 1, -1);
    CGContextSetTextMatrix(cgContext, CGAffineTransformIdentity);

#if USE_DELEGATE
        CTLineDraw(line, cgContext);
#else
    if (!adv_x)
        CTLineDraw(line, cgContext);
    else
        __DrawLine(text_part.text, line, cgContext, adv_x);
#endif

    if ( text_part.pFont->GetUnderlined() ) {
        //AKT: draw horizontal line 1 pixel thick and with 1 pixel gap under baseline
        CGFloat width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);

        CGPoint points[] = { {0.0, -2.0},  {width, -2.0} };

        CGContextSetStrokeColorWithColor(cgContext, col);
        CGContextSetShouldAntialias(cgContext, false);
        CGContextSetLineWidth(cgContext, 1.0);
        CGContextStrokeLineSegments(cgContext, points, 2);
    }
    if ( text_part.pFont->GetStrikethrough() )
    {
        CGFloat width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
        CGFloat height = CTFontGetXHeight( font );
        CGPoint points[] = { {0.0, height * 0.6},  {width, height * 0.6} };
        CGContextSetStrokeColorWithColor(cgContext, col);
        CGContextSetShouldAntialias(cgContext, false);
        CGContextSetLineWidth(cgContext, 1.0);
        CGContextStrokeLineSegments(cgContext, points, 2);
    }

    CGContextRestoreGState(cgContext);
    CGContextSetTextMatrix(cgContext, textMatrix);
    CGColorRelease( col );
}

#if !USE_DELEGATE
static
void __ApplyStyles(CTRunRef run, CGContextRef context) {
    CFDictionaryRef attributes = CTRunGetAttributes(run);

    // Set the font
    CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(attributes, kCTFontAttributeName);
    CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL);
    CGContextSetFont(context, cgFont);
    CGContextSetFontSize(context, CTFontGetSize(runFont));
    CFRelease(cgFont);

    // Set the color
    CGColorRef color = (CGColorRef)CFDictionaryGetValue(attributes, kCTForegroundColorAttributeName);
    CGContextSetFillColorWithColor(context, color);
}

static
void __DrawRun(const wxString & text, CTRunRef run, CGContextRef cgContext, double adv_x) {
    __ApplyStyles(run, cgContext);

    CFRange range = CTRunGetStringRange(run);
    wxWCharBuffer buf = text.wc_str();

    std::vector<CGPoint> positions;
    std::vector<CGGlyph> glyphs;

    CFDictionaryRef attributes = CTRunGetAttributes(run);
    CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(attributes, kCTFontAttributeName);

    CGPoint pt = CGPointMake(0, 0);

    for(size_t i = 0; i < buf.length(); i++) {
        wchar_t ch = buf[(size_t)i];

        if ((CFIndex)i < range.location) {
            pt.x += (char_width(ch) > 1 ? adv_x * 2 : adv_x);
            continue;
        }

        if ((CFIndex)i >= range.location + range.length)
            break;

        CGGlyph glyph;
        CTFontGetGlyphsForCharacters(runFont, (UniChar*)&ch, &glyph, 1);

        positions.push_back(pt);
        glyphs.push_back(glyph);

        pt.x += (char_width(ch) > 1 ? adv_x * 2 : adv_x);
    }

    if (glyphs.size() > 0) {
        CGContextShowGlyphsAtPositions(cgContext, &glyphs[0], &positions[0], glyphs.size());
    }
}

static
void __DrawLine(const wxString & text, CTLineRef line, CGContextRef cgContext, double adv_x) {
    (void)line;
    (void)cgContext;
    (void)adv_x;

    CFArrayRef runs = CTLineGetGlyphRuns(line);

    for(CFIndex i=0;i < CFArrayGetCount(runs); i++) {
        CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i);

        __DrawRun(text, run, cgContext, adv_x);
    }
}
#endif
<|endoftext|>