about summary refs log tree commit diff stats
path: root/cpp/014boolean
blob: e6dd12bab30fc271c21eef606acdbaa7501801d1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
:(before "End Globals")
// Boolean ops.
const int AND = 7;
:(before "End Primitive Recipe Numbers")
Recipe_number["and"] = AND;
assert(Next_recipe_number == AND);
Next_recipe_number++;
:(before "End Primitive Recipe Implementations")
case AND: {
  trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name;
  vector<int> arg0 = read_memory(instructions[pc].ingredients[0]);
  assert(arg0.size() == 1);
  trace("run") << "ingredient 1 is " << instructions[pc].ingredients[1].name;
  vector<int> arg1 = read_memory(instructions[pc].ingredients[1]);
  assert(arg1.size() == 1);
  vector<int> result;
  result.push_back(arg0[0] && arg1[0]);
  trace("run") << "product 0 is " << result[0];
  write_memory(instructions[pc].products[0], result);
  break;
}

:(scenario "and")
recipe main [
  1:integer <- copy 1:literal
  2:integer <- copy 0:literal
  3:integer <- and 1:integer, 2:integer
]
+run: instruction main/2
+run: ingredient 0 is 1
+mem: location 1 is 1
+run: ingredient 1 is 2
+mem: location 2 is 0
+run: product 0 is 0
+mem: storing in location 3

:(before "End Globals")
const int OR = 8;
:(before "End Primitive Recipe Numbers")
Recipe_number["or"] = OR;
assert(Next_recipe_number == OR);
Next_recipe_number++;
:(before "End Primitive Recipe Implementations")
case OR: {
  trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name;
  vector<int> arg0 = read_memory(instructions[pc].ingredients[0]);
  assert(arg0.size() == 1);
  trace("run") << "ingredient 1 is " << instructions[pc].ingredients[1].name;
  vector<int> arg1 = read_memory(instructions[pc].ingredients[1]);
  assert(arg1.size() == 1);
  vector<int> result;
  result.push_back(arg0[0] || arg1[0]);
  trace("run") << "product 0 is " << result[0];
  write_memory(instructions[pc].products[0], result);
  break;
}

:(scenario "or")
recipe main [
  1:integer <- copy 1:literal
  2:integer <- copy 0:literal
  3:integer <- or 1:integer, 2:integer
]
+run: instruction main/2
+run: ingredient 0 is 1
+mem: location 1 is 1
+run: ingredient 1 is 2
+mem: location 2 is 0
+run: product 0 is 1
+mem: storing in location 3

:(before "End Globals")
const int NOT = 9;
:(before "End Primitive Recipe Numbers")
Recipe_number["not"] = NOT;
assert(Next_recipe_number == NOT);
Next_recipe_number++;
:(before "End Primitive Recipe Implementations")
case NOT: {
  trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name;
  vector<int> arg0 = read_memory(instructions[pc].ingredients[0]);
  assert(arg0.size() == 1);
  vector<int> result;
  result.push_back(!arg0[0]);
  trace("run") << "product 0 is " << result[0];
  write_memory(instructions[pc].products[0], result);
  break;
}

:(scenario "not")
recipe main [
  1:integer <- copy 1:literal
  2:integer <- not 1:integer
]
+run: instruction main/1
+run: ingredient 0 is 1
+mem: location 1 is 1
+run: product 0 is 0
+mem: storing in location 2
} /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#
#
#            Nim's Runtime Library
#        (c) Copyright 2015 Nim Contributors
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## :Authors: Zahary Karadjov
##
## This module provides utilities for reserving a portions of the
## address space of a program without consuming physical memory.
## It can be used to implement a dynamically resizable buffer that
## is guaranteed to remain in the same memory location. The buffer
## will be able to grow up to the size of the initially reserved
## portion of the address space.

from ospaths import raiseOSError, osLastError

template distance*(lhs, rhs: pointer): int =
  cast[int](rhs) - cast[int](lhs)

template shift*(p: pointer, distance: int): pointer =
  cast[pointer](cast[int](p) + distance)

type
  MemAccessFlags* = int

  ReservedMem* = object
    memStart: pointer
    usedMemEnd: pointer
    committedMemEnd: pointer
    memEnd: pointer
    maxCommittedAndUnusedPages: int
    accessFlags: MemAccessFlags

  ReservedMemSeq*[T] = object
    mem: ReservedMem

when defined(windows):
  import winlean

  type
    SYSTEM_INFO {.final, pure.} = object
      u1: uint32
      dwPageSize: uint32
      lpMinimumApplicationAddress: pointer
      lpMaximumApplicationAddress: pointer
      dwActiveProcessorMask: ptr uint32
      dwNumberOfProcessors: uint32
      dwProcessorType: uint32
      dwAllocationGranularity: uint32
      wProcessorLevel: uint16
      wProcessorRevision: uint16

  proc getSystemInfo(lpSystemInfo: ptr SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".}

  proc getAllocationGranularity: uint =
    var sysInfo: SYSTEM_INFO
    getSystemInfo(addr sysInfo)
    return uint(sysInfo.dwAllocationGranularity)

  let allocationGranularity = getAllocationGranularity().int

  const
    memNoAccess       = MemAccessFlags(PAGE_NOACCESS)
    memExec*          = MemAccessFlags(PAGE_EXECUTE)
    memExecRead*      = MemAccessFlags(PAGE_EXECUTE_READ)
    memExecReadWrite* = MemAccessFlags(PAGE_EXECUTE_READWRITE)
    memRead*          = MemAccessFlags(PAGE_READONLY)
    memReadWrite*     = MemAccessFlags(PAGE_READWRITE)

  template check(expr) =
    let r = expr
    if r == cast[type(r)](0):
      raiseOSError(osLastError())

else:
  import posix

  let allocationGranularity = sysconf(SC_PAGESIZE)

  let
    memNoAccess       = MemAccessFlags(PROT_NONE)
    memExec*          = MemAccessFlags(PROT_EXEC)
    memExecRead*      = MemAccessFlags(PROT_EXEC or PROT_READ)
    memExecReadWrite* = MemAccessFlags(PROT_EXEC or PROT_READ or PROT_WRITE)
    memRead*          = MemAccessFlags(PROT_READ)
    memReadWrite*     = MemAccessFlags(PROT_READ or PROT_WRITE)

  template check(expr) =
    if not expr:
      raiseOSError(osLastError())

func nextAlignedOffset(n, alignment: int): int =
  result = n
  let m = n mod alignment
  if m != 0: result += alignment - m


when defined(windows):
  const
    MEM_DECOMMIT = 0x4000
    MEM_RESERVE = 0x2000
    MEM_COMMIT = 0x1000
  proc virtualFree(lpAddress: pointer, dwSize: int,
                   dwFreeType: int32): cint {.header: "<windows.h>", stdcall,
                   importc: "VirtualFree".}
  proc virtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
                    flProtect: int32): pointer {.
                    header: "<windows.h>", stdcall, importc: "VirtualAlloc".}

proc init*(T: type ReservedMem,
           maxLen: Natural,
           initLen: Natural = 0,
           initCommitLen = initLen,
           memStart = pointer(nil),
           accessFlags = memReadWrite,
           maxCommittedAndUnusedPages = 3): ReservedMem =

  assert initLen <= initCommitLen
  let commitSize = nextAlignedOffset(initCommitLen, allocationGranularity)

  when defined(windows):
    result.memStart = virtualAlloc(memStart, maxLen, MEM_RESERVE, accessFlags.cint)
    check result.memStart
    if commitSize > 0:
      check virtualAlloc(result.memStart, commitSize, MEM_COMMIT, accessFlags.cint)
  else:
    var allocFlags = MAP_PRIVATE or MAP_ANONYMOUS # or MAP_NORESERVE
    # if memStart != nil:
    #  allocFlags = allocFlags or MAP_FIXED_NOREPLACE
    result.memStart = mmap(memStart, maxLen, PROT_NONE, allocFlags, -1, 0)
    check result.memStart != MAP_FAILED
    if commitSize > 0:
      check mprotect(result.memStart, commitSize, cint(accessFlags)) == 0

  result.usedMemEnd = result.memStart.shift(initLen)
  result.committedMemEnd = result.memStart.shift(commitSize)
  result.memEnd = result.memStart.shift(maxLen)
  result.accessFlags = accessFlags
  result.maxCommittedAndUnusedPages = maxCommittedAndUnusedPages

func len*(m: ReservedMem): int =
  distance(m.memStart, m.usedMemEnd)

func commitedLen*(m: ReservedMem): int =
  distance(m.memStart, m.committedMemEnd)

func maxLen*(m: ReservedMem): int =
  distance(m.memStart, m.memEnd)

proc setLen*(m: var ReservedMem, newLen: int) =
  let len = m.len
  m.usedMemEnd = m.memStart.shift(newLen)
  if newLen > len:
    let d = distance(m.committedMemEnd, m.usedMemEnd)
    if d > 0:
      let commitExtensionSize = nextAlignedOffset(d, allocationGranularity)
      when defined(windows):
        check virtualAlloc(m.committedMemEnd, commitExtensionSize,
                           MEM_COMMIT, m.accessFlags.cint)
      else:
        check mprotect(m.committedMemEnd, commitExtensionSize, m.accessFlags.cint) == 0
  else:
    let d = distance(m.usedMemEnd, m.committedMemEnd) -
            m.maxCommittedAndUnusedPages * allocationGranularity
    if d > 0:
      let commitSizeShrinkage = nextAlignedOffset(d, allocationGranularity)
      let newCommitEnd = m.committedMemEnd.shift(-commitSizeShrinkage)

      when defined(windows):
        check virtualFree(newCommitEnd, commitSizeShrinkage, MEM_DECOMMIT)
      else:
        check posix_madvise(newCommitEnd, commitSizeShrinkage,
                            POSIX_MADV_DONTNEED) == 0

      m.committedMemEnd = newCommitEnd

proc init*(SeqType: type ReservedMemSeq,
           maxLen: Natural,
           initLen: Natural = 0,
           initCommitLen: Natural = 0,
           memStart = pointer(nil),
           accessFlags = memReadWrite,
           maxCommittedAndUnusedPages = 3): SeqType =

  let elemSize = sizeof(SeqType.T)
  result.mem = ReservedMem.init(maxLen * elemSize,
                                initLen * elemSize,
                                initCommitLen * elemSize,
                                memStart, accessFlags,
                                maxCommittedAndUnusedPages)

func `[]`*[T](s: ReservedMemSeq[T], pos: Natural): lent T =
  let elemAddr = s.mem.memStart.shift(pos * sizeof(T))
  rangeCheck elemAddr < s.mem.usedMemEnd
  result = (cast[ptr T](elemAddr))[]

func `[]`*[T](s: var ReservedMemSeq[T], pos: Natural): var T =
  let elemAddr = s.mem.memStart.shift(pos * sizeof(T))
  rangeCheck elemAddr < s.mem.usedMemEnd
  result = (cast[ptr T](elemAddr))[]

func `[]`*[T](s: ReservedMemSeq[T], rpos: BackwardsIndex): lent T =
  return s[int(s.len) - int(rpos)]

func `[]`*[T](s: var ReservedMemSeq[T], rpos: BackwardsIndex): var T =
  return s[int(s.len) - int(rpos)]

func len*[T](s: ReservedMemSeq[T]): int =
  s.mem.len div sizeof(T)

proc setLen*[T](s: var ReservedMemSeq[T], newLen: int) =
  # TODO call destructors
  s.mem.setLen(newLen * sizeof(T))

proc add*[T](s: var ReservedMemSeq[T], val: T) =
  let len = s.len
  s.setLen(len + 1)
  s[len] = val

proc pop*[T](s: var ReservedMemSeq[T]): T =
  assert s.usedMemEnd != s.memStart
  let lastIdx = s.len - 1
  result = s[lastIdx]
  s.setLen(lastIdx)

func commitedLen*[T](s: ReservedMemSeq[T]): int =
  s.mem.commitedLen div sizeof(T)

func maxLen*[T](s: ReservedMemSeq[T]): int =
  s.mem.maxLen div sizeof(T)