< Summary

Class:Itinero.Network.Tiles.NetworkTile
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Attributes.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Geo.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Serialization.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.TurnCosts.cs
Covered lines:816
Uncovered lines:114
Coverable lines:930
Total lines:1472
Line coverage:87.7% (816 of 930)
Covered branches:230
Total branches:278
Branch coverage:82.7% (230 of 278)
Tag:224_14471318300

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%1100%
SetAttributes(...)87.5%881.25%
GetAttributes()100%6100%
AddOrGetString(...)100%6100%
WriteAttributesTo(...)100%4100%
ReadAttributesFrom(...)100%4100%
.ctor(...)100%2100%
.ctor(...)100%1100%
Clone()100%1100%
get_TileId()100%1100%
get_VertexCount()100%1100%
get_EdgeTypeMapId()100%1100%
AddVertex(...)100%1100%
HasVertex(...)100%2100%
TryGetVertex(...)100%2100%
AddEdge(...)81.81%2291.42%
DeleteEdge(...)100%2100%
IsEdgeDeleted(...)100%2100%
get_HasDeletedEdges()100%1100%
RemoveDeletedEdges()83.33%2488.73%
CloneForEdgeTypeMap(...)66.66%1283.07%
VertexEdgePointer(...)100%1100%
EncodeVertex(...)100%6100%
DecodeVertex(...)100%2100%
DecodeEdgeCrossId(...)100%1100%
GetEdgeCrossPointer(...)100%1100%
EncodePointer(...)100%2100%
DecodePointer(...)100%1100%
SetDynamicUIn32Nullable(...)50%257.14%
GetTailHeadOrder(...)100%1100%
DecodeEdgePointerId(...)100%1100%
WriteEdgesAndVerticesTo(...)83.33%682.35%
ReadEdgesAndVerticesFrom(...)83.33%685%
.ctor(...)100%1100%
SetCoordinate(...)77.77%1876.31%
GetCoordinate(...)100%4100%
SetShape(...)93.75%1696.77%
GetShape()86.36%2291.83%
WriteGeoTo(...)100%6100%
ReadGeoFrom(...)100%6100%
WriteTo(...)100%1100%
ReadFrom(...)50%286.66%
.ctor(...)100%1100%
AddTurnCosts(...)82.5%4081.44%
GetTurnCosts()68.18%2263.79%
SetTailHeadOrder(...)64.28%1482.85%
WriteTurnCostsTo(...)50%450%
ReadTurnCostsFrom(...)50%457.14%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Attributes.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using System.IO;
 3using Itinero.Data;
 4using Itinero.IO;
 5using Itinero.Network.Storage;
 6using Reminiscence.Arrays;
 7
 8namespace Itinero.Network.Tiles;
 9
 10internal partial class NetworkTile
 11{
 12    /// <summary>
 13    /// Stores the attributes, starting with the number of attributes and then alternating key-value pairs.
 14    /// </summary>
 15    private readonly ArrayBase<byte> _attributes;
 16
 21017    private uint _nextAttributePointer = 0;
 18
 19    /// <summary>
 20    /// Stores each string once.
 21    /// </summary>
 22    private readonly ArrayBase<string> _strings;
 23
 21024    private uint _nextStringId = 0;
 25
 26    private uint SetAttributes(IEnumerable<(string key, string value)> attributes)
 12627    {
 12628        var start = _nextAttributePointer;
 29
 12630        long cPos = start;
 12631        long p = start + 1;
 12632        var c = 0;
 52833        foreach (var (key, value) in attributes)
 7534        {
 7535            if (_attributes.Length <= p + 16)
 3836            {
 3837                _attributes.Resize(_attributes.Length + 256);
 3838            }
 39
 7540            var id = this.AddOrGetString(key);
 7541            p += _attributes.SetDynamicUInt32(p, id);
 7542            id = this.AddOrGetString(value);
 7543            p += _attributes.SetDynamicUInt32(p, id);
 44
 7545            c++;
 7546            if (c == 255)
 047            {
 048                _attributes[cPos] = 255;
 049                c = 0;
 050                cPos = p;
 051                p++;
 052            }
 7553        }
 54
 12655        if (_attributes.Length <= cPos)
 5156        {
 5157            _attributes.Resize(_attributes.Length + 256);
 5158        }
 59
 12660        _attributes[cPos] = (byte)c;
 61
 12662        _nextAttributePointer = (uint)p;
 63
 12664        return start;
 12665    }
 66
 67    internal IEnumerable<(string key, string value)> GetAttributes(uint? pointer)
 11768    {
 11769        if (pointer == null)
 4470        {
 4471            yield break;
 72        }
 73
 7374        var p = pointer.Value;
 75
 7376        var count = -1;
 77        do
 7378        {
 7379            count = _attributes[p];
 7380            p++;
 81
 24682            for (var i = 0; i < count; i++)
 7983            {
 7984                p += (uint)_attributes.GetDynamicUInt32(p, out var keyId);
 7985                p += (uint)_attributes.GetDynamicUInt32(p, out var valId);
 86
 7987                yield return (_strings[keyId], _strings[valId]);
 5088            }
 8889        } while (count == 255);
 4490    }
 91
 92    private uint AddOrGetString(string s)
 15093    {
 69694        for (uint i = 0; i < _nextStringId; i++)
 22695        {
 22696            var existing = _strings[i];
 22697            if (existing == s)
 2898            {
 2899                return i;
 100            }
 198101        }
 102
 122103        if (_strings.Length <= _nextStringId)
 38104        {
 38105            _strings.Resize(_strings.Length + 256);
 38106        }
 107
 122108        var id = _nextStringId;
 122109        _nextStringId++;
 110
 122111        _strings[id] = s;
 122112        return id;
 150113    }
 114
 115    private void WriteAttributesTo(Stream stream)
 8116    {
 8117        stream.WriteVarUInt32(_nextAttributePointer);
 40118        for (var i = 0; i < _nextAttributePointer; i++)
 12119        {
 12120            stream.WriteByte(_attributes[i]);
 12121        }
 122
 8123        stream.WriteVarUInt32(_nextStringId);
 36124        for (var i = 0; i < _nextStringId; i++)
 10125        {
 10126            stream.WriteWithSize(_strings[i]);
 10127        }
 8128    }
 129
 130    private void ReadAttributesFrom(Stream stream)
 8131    {
 8132        _nextAttributePointer = stream.ReadVarUInt32();
 8133        _attributes.Resize(_nextAttributePointer);
 40134        for (var i = 0; i < _nextAttributePointer; i++)
 12135        {
 12136            _attributes[i] = (byte)stream.ReadByte();
 12137        }
 138
 8139        _nextStringId = stream.ReadVarUInt32();
 8140        _strings.Resize(_nextStringId);
 36141        for (var i = 0; i < _nextStringId; i++)
 10142        {
 10143            _strings[i] = stream.ReadWithSizeString();
 10144        }
 8145    }
 146}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using Itinero.IO;
 5using Itinero.Network.Storage;
 6using Itinero.Network.TurnCosts;
 7using Reminiscence.Arrays;
 8
 9namespace Itinero.Network.Tiles;
 10
 11internal partial class NetworkTile
 12{
 13    private const int DefaultSizeIncrease = 16;
 14
 15    private readonly uint _tileId;
 16    private readonly int _zoom; // the zoom level.
 17    private readonly Guid _edgeTypeMapId; // the edge type index id.
 18
 21019    private uint _nextVertexId = 0; // the next vertex id.
 20
 21    // the pointers, per vertex, to their first edge.
 22    // TODO: investigate if it's worth storing these with less precision, one tile will never contain this much data.
 23    // TODO: investigate if we can not use one-increasing vertex ids but also use their pointers like with the edges.
 24    private readonly ArrayBase<uint> _pointers;
 25    private uint _nextCrossTileId; // the next id for an edge that crosses tile boundaries.
 26    private readonly ArrayBase<uint> _crossEdgePointers; // points to the cross tile boundary edges.
 27
 28    // the next edge id.
 21029    private uint _nextEdgeId = 0;
 30
 31    // the edges.
 32    private readonly ArrayBase<byte> _edges;
 33
 34    /// <summary>
 35    /// Creates a new tile.
 36    /// </summary>
 37    /// <param name="zoom">The zoom level.</param>
 38    /// <param name="tileId">The tile id.</param>
 39    /// <param name="edgeTypeMapId">The edge type index id.</param>
 19440    public NetworkTile(int zoom, uint tileId, Guid? edgeTypeMapId = null)
 19441    {
 19442        _zoom = zoom;
 19443        _tileId = tileId;
 19444        _edgeTypeMapId = edgeTypeMapId ?? Guid.Empty;
 19445        _nextCrossTileId = 0;
 46
 19447        _pointers = new MemoryArray<uint>(0);
 19448        _edges = new MemoryArray<byte>(0);
 19449        _crossEdgePointers = new MemoryArray<uint>(0);
 50
 19451        _coordinates = new MemoryArray<byte>(0);
 19452        _shapes = new MemoryArray<byte>(0);
 19453        _attributes = new MemoryArray<byte>(0);
 19454        _strings = new MemoryArray<string>(0);
 19455    }
 56
 1657    private NetworkTile(int zoom, uint tileId, Guid edgeTypeMapId, uint nextCrossTileId, ArrayBase<uint> pointers,
 1658        ArrayBase<byte> edges,
 1659        ArrayBase<uint> crossEdgePointers, ArrayBase<byte> coordinates, ArrayBase<byte> shapes,
 1660        ArrayBase<byte> attributes,
 1661        ArrayBase<string> strings, ArrayBase<byte> turnCosts, uint nextVertexId, uint nextEdgeId,
 1662        uint nextAttributePointer,
 1663        uint nextShapePointer, uint nextStringId)
 1664    {
 1665        _zoom = zoom;
 1666        _tileId = tileId;
 1667        _edgeTypeMapId = edgeTypeMapId;
 1668        _nextCrossTileId = nextCrossTileId;
 1669        _pointers = pointers;
 1670        _edges = edges;
 1671        _crossEdgePointers = crossEdgePointers;
 72
 1673        _coordinates = coordinates;
 1674        _shapes = shapes;
 1675        _attributes = attributes;
 1676        _strings = strings;
 1677        _turnCosts = turnCosts;
 78
 1679        _nextVertexId = nextVertexId;
 1680        _nextEdgeId = nextEdgeId;
 1681        _nextAttributePointer = nextAttributePointer;
 1682        _nextShapePointer = nextShapePointer;
 1683        _nextStringId = nextStringId;
 1684    }
 85
 86    /// <summary>
 87    /// Clones this graph tile.
 88    /// </summary>
 89    /// <returns>The copy of this tile.</returns>
 90    public NetworkTile Clone()
 1091    {
 1092        return new(_zoom, _tileId, _edgeTypeMapId, _nextCrossTileId, _pointers.Clone(), _edges.Clone(),
 1093            _crossEdgePointers.Clone(),
 1094            _coordinates.Clone(), _shapes.Clone(), _attributes.Clone(), _strings.Clone(), _turnCosts.Clone(),
 1095            _nextVertexId,
 1096            _nextEdgeId, _nextAttributePointer, _nextShapePointer, _nextStringId);
 1097    }
 98
 99    /// <summary>
 100    /// Gets the tile id.
 101    /// </summary>
 2726102    public uint TileId => _tileId;
 103
 104    /// <summary>
 105    /// Gets the number of vertices.
 106    /// </summary>
 362107    public uint VertexCount => _nextVertexId;
 108
 109    /// <summary>
 110    /// Gets the edge type map id.
 111    /// </summary>
 988112    public Guid EdgeTypeMapId => _edgeTypeMapId;
 113
 114    /// <summary>
 115    /// Adds a new vertex and returns its id.
 116    /// </summary>
 117    /// <param name="longitude">The longitude.</param>
 118    /// <param name="latitude">The latitude.</param>
 119    /// <param name="e">The elevation in meters.</param>
 120    /// <returns>The ID of the new vertex.</returns>
 121    public VertexId AddVertex(double longitude, double latitude, float? e = null)
 433122    {
 123        // set coordinate.
 433124        this.SetCoordinate(_nextVertexId, longitude, latitude, e);
 125
 126        // create id.
 433127        var vertexId = new VertexId(_tileId, _nextVertexId);
 433128        _nextVertexId++;
 129
 130        // make room.
 433131        _pointers.EnsureMinimumSize(vertexId.LocalId, DefaultSizeIncrease);
 132
 433133        return vertexId;
 433134    }
 135
 136    /// <summary>
 137    /// Returns true if the vertex exists.
 138    /// </summary>
 139    /// <param name="vertex"></param>
 140    /// <returns></returns>
 141    public bool HasVertex(VertexId vertex)
 34142    {
 34143        if (vertex.LocalId >= _nextVertexId)
 11144        {
 11145            return false;
 146        }
 147
 23148        return true;
 34149    }
 150
 151    /// <summary>
 152    /// Gets the vertex with the given id.
 153    /// </summary>
 154    /// <param name="vertex">The vertex.</param>
 155    /// <param name="longitude">The longitude.</param>
 156    /// <param name="latitude">The latitude.</param>
 157    /// <param name="elevation">The elevation.</param>
 158    /// <returns>True if the vertex exists.</returns>
 159    public bool TryGetVertex(VertexId vertex, out double longitude, out double latitude, out float? elevation)
 691160    {
 691161        longitude = default;
 691162        latitude = default;
 691163        elevation = null;
 691164        if (vertex.LocalId >= _nextVertexId)
 11165        {
 11166            return false;
 167        }
 168
 680169        this.GetCoordinate(vertex.LocalId, out longitude, out latitude, out elevation);
 680170        return true;
 691171    }
 172
 173    /// <summary>
 174    /// Adds a new edge and returns its id.
 175    /// </summary>
 176    /// <param name="vertex1">The first vertex.</param>
 177    /// <param name="vertex2">The second vertex.</param>
 178    /// <param name="shape">The shape."</param>
 179    /// <param name="attributes">The attributes."</param>
 180    /// <param name="edgeId">The edge id if this edge is a part of another tile.</param>
 181    /// <param name="edgeTypeId">The edge type id, if any.</param>
 182    /// <param name="length">The length in centimeters.</param>
 183    /// <returns>The new edge id.</returns>
 184    public EdgeId AddEdge(VertexId vertex1, VertexId vertex2,
 185        IEnumerable<(double longitude, double latitude, float? e)>? shape = null,
 186        IEnumerable<(string key, string value)>? attributes = null, EdgeId? edgeId = null, uint? edgeTypeId = null,
 187        uint? length = null)
 270188    {
 270189        if (vertex2.TileId != _tileId)
 7190        {
 191            // this edge crosses tiles boundaries, it need special treatment and a stable id.
 192            // because the edge originates in this tile, this tile is responsible for generating the id.
 7193            if (edgeId != null) throw new ArgumentException(
 0194                    "The edge id shouldn't be a given, it should be generated by the originating tile.",
 0195                    nameof(edgeId));
 7196            if (vertex1.TileId != _tileId) throw new ArgumentException("None of the two vertices in this edge are in thi
 0197                    nameof(vertex1));
 198
 199            // generate a new cross tile id and store pointer to edge.
 7200            edgeId = EdgeId.CrossEdgeId(_tileId, _nextCrossTileId);
 7201            _crossEdgePointers.EnsureMinimumSize(_nextCrossTileId + 1);
 7202            _crossEdgePointers[_nextCrossTileId] = _nextEdgeId;
 7203            _nextCrossTileId++;
 7204        }
 263205        else if (vertex1.TileId != _tileId)
 6206        {
 207            // this edge crosses tiles boundaries, it need special treatment and a stable id.
 208            // because the edge originates in another tile it should already have an id.
 6209            if (edgeId == null) throw new ArgumentException(
 0210                    "Cannot add an edge that doesn't start in this tile without a proper edge id.",
 0211                    nameof(edgeId));
 6212            if (edgeId.Value.TileId != vertex1.TileId) throw new ArgumentException("The edge id doesn't match the tile i
 0213                    nameof(edgeId));
 6214        }
 215        else
 257216        {
 217            // this edge starts in this tile, it get an id from this tile.
 257218            edgeId = new EdgeId(_tileId, _nextEdgeId);
 257219        }
 220
 221        // write the edge data.
 270222        var newEdgePointer = _nextEdgeId;
 270223        var size = EncodeVertex(_edges, _tileId, _nextEdgeId, vertex1);
 270224        _nextEdgeId += size;
 270225        size = EncodeVertex(_edges, _tileId, _nextEdgeId, vertex2);
 270226        _nextEdgeId += size;
 227
 228        // get previous pointers if vertex already has edges
 229        // set the new pointers.
 270230        uint? v1p = null;
 270231        if (vertex1.TileId == _tileId)
 264232        {
 264233            v1p = _pointers[vertex1.LocalId].DecodeNullableData();
 264234            _pointers[vertex1.LocalId] = newEdgePointer.EncodeToNullableData();
 264235        }
 236
 270237        uint? v2p = null;
 270238        if (vertex2.TileId == _tileId)
 263239        {
 263240            v2p = _pointers[vertex2.LocalId].DecodeNullableData();
 263241            _pointers[vertex2.LocalId] = newEdgePointer.EncodeToNullableData();
 263242        }
 243
 244        // set next pointers.
 270245        size = EncodePointer(_edges, _nextEdgeId, v1p);
 270246        _nextEdgeId += size;
 270247        size = EncodePointer(_edges, _nextEdgeId, v2p);
 270248        _nextEdgeId += size;
 249
 250        // write edge id explicitly if not in this edge.
 270251        if (vertex1.TileId != vertex2.TileId)
 13252        {
 253            // this data will only be there for edges crossing tile boundaries.
 13254            _nextEdgeId += (uint)_edges.SetDynamicUInt32(_nextEdgeId,
 13255                edgeId.Value.LocalId - EdgeId.MinCrossId);
 13256        }
 257
 258        // write edge profile id.
 270259        _nextEdgeId += SetDynamicUIn32Nullable(_edges, _nextEdgeId, edgeTypeId);
 260
 261        // write length.
 270262        _nextEdgeId += SetDynamicUIn32Nullable(_edges, _nextEdgeId, length);
 263
 264        // set tail and head order.
 270265        _edges.SetTailHeadOrder(_nextEdgeId, null, null);
 270266        _nextEdgeId++;
 267
 268        // take care of shape if any.
 270269        uint? shapePointer = null;
 270270        if (shape != null)
 88271        {
 88272            shapePointer = this.SetShape(shape);
 88273        }
 274
 270275        size = EncodePointer(_edges, _nextEdgeId, shapePointer);
 270276        _nextEdgeId += size;
 277
 278        // take care of attributes if any.
 270279        uint? attributesPointer = null;
 270280        if (attributes != null)
 113281        {
 113282            attributesPointer = this.SetAttributes(attributes);
 113283        }
 284
 270285        size = EncodePointer(_edges, _nextEdgeId, attributesPointer);
 270286        _nextEdgeId += size;
 287
 270288        return edgeId.Value;
 270289    }
 290
 291    private HashSet<EdgeId>? _deletedEdges;
 292
 293    /// <summary>
 294    /// Marks the given edge as deleted.
 295    /// </summary>
 296    /// <param name="edge">The edge to delete.</param>
 297    internal void DeleteEdge(EdgeId edge)
 10298    {
 10299        _deletedEdges ??= new HashSet<EdgeId>();
 10300        _deletedEdges.Add(edge);
 10301    }
 302
 303    /// <summary>
 304    /// Returns true if the edge is deleted.
 305    /// </summary>
 306    /// <param name="edge">The edge.</param>
 307    /// <returns>True if the edge was deleted.</returns>
 308    internal bool IsEdgeDeleted(EdgeId edge)
 1105309    {
 2204310        if (_deletedEdges == null) return false;
 311
 6312        return _deletedEdges.Contains(edge);
 1105313    }
 314
 315    /// <summary>
 316    /// Returns true if there are deleted edges.
 317    /// </summary>
 123318    internal bool HasDeletedEdges => _deletedEdges != null;
 319
 320    /// <summary>
 321    /// Removes all the deleted edges.
 322    /// </summary>
 323    /// <remarks>This changes all the edges if all edges written after a deleted edge.</remarks>
 324    internal void RemoveDeletedEdges()
 10325    {
 10326        if (_deletedEdges == null) return;
 327
 328        // reset vertex pointers.
 340329        for (var i = 0; i < _pointers.Length; i++)
 160330        {
 160331            _pointers[i] = 0;
 160332        }
 333
 334        // redo edges, skipping deleted edges.
 10335        var nextEdgeId = _nextEdgeId;
 10336        var p = 0U;
 10337        var newP = 0U;
 24338        while (p < nextEdgeId)
 14339        {
 340            // read edge data.
 14341            var currentEdgeId = p;
 14342            p += this.DecodeVertex(p, out var local1Id, out var tile1Id);
 14343            var vertex1 = new VertexId(tile1Id, local1Id);
 14344            p += this.DecodeVertex(p, out var local2Id, out var tile2Id);
 14345            var vertex2 = new VertexId(tile2Id, local2Id);
 14346            p += this.DecodePointer(p, out _);
 14347            p += this.DecodePointer(p, out _);
 14348            uint? crossEdgeId = null;
 14349            if (tile1Id != tile2Id)
 4350            {
 4351                p += (uint)_edges.GetDynamicUInt32(p, out var c);
 4352                crossEdgeId = c;
 4353            }
 354
 14355            p += (uint)_edges.GetDynamicUInt32Nullable(p, out var edgeTypeId);
 14356            p += (uint)_edges.GetDynamicUInt32Nullable(p, out var length);
 14357            var tailHeadOrder = _edges[p];
 14358            p++;
 14359            p += this.DecodePointer(p, out var shapePointer);
 14360            p += this.DecodePointer(p, out var attributePointer);
 361
 362            // check if edge was deleted.
 20363            if (_deletedEdges.Contains(new EdgeId(_tileId, currentEdgeId))) continue;
 8364            if (crossEdgeId.HasValue)
 4365            {
 8366                if (_deletedEdges.Contains(EdgeId.CrossEdgeId(vertex1.TileId, crossEdgeId.Value))) continue;
 0367            }
 368
 369            // no need to overwrite identical data.
 4370            if (p == newP) continue;
 371
 372            // write edge data again.
 4373            var newEdgePointer = newP;
 4374            newP += EncodeVertex(_edges, _tileId, newP, vertex1);
 4375            newP += EncodeVertex(_edges, _tileId, newP, vertex2);
 4376            uint? v1p = null;
 4377            if (vertex1.TileId == _tileId)
 4378            {
 4379                v1p = _pointers[vertex1.LocalId].DecodeNullableData();
 4380                _pointers[vertex1.LocalId] = newEdgePointer.EncodeToNullableData();
 4381            }
 382
 4383            uint? v2P = null;
 4384            if (vertex2.TileId == _tileId)
 4385            {
 4386                v2P = _pointers[vertex2.LocalId].DecodeNullableData();
 4387                _pointers[vertex2.LocalId] = newEdgePointer.EncodeToNullableData();
 4388            }
 389
 4390            newP += EncodePointer(_edges, newP, v1p);
 4391            newP += EncodePointer(_edges, newP, v2P);
 4392            if (crossEdgeId != null)
 0393            {
 0394                newP += (uint)_edges.SetDynamicUInt32(newP, crossEdgeId.Value);
 0395                if (vertex1.TileId == _tileId)
 0396                {
 0397                    _crossEdgePointers[crossEdgeId.Value] = newEdgePointer;
 0398                }
 0399            }
 400
 4401            newP += (uint)_edges.SetDynamicUInt32Nullable(newP, edgeTypeId);
 4402            newP += (uint)_edges.SetDynamicUInt32Nullable(newP, length);
 4403            _edges[newP] = tailHeadOrder;
 4404            newP++;
 4405            newP += EncodePointer(_edges, newP, shapePointer);
 4406            newP += EncodePointer(_edges, newP, attributePointer);
 4407        }
 408
 10409        _nextEdgeId = newP;
 10410        _deletedEdges = null;
 10411    }
 412
 413    internal NetworkTile CloneForEdgeTypeMap(
 414        (Guid id, Func<IEnumerable<(string key, string value)>, uint> func) edgeTypeMap)
 6415    {
 6416        var edges = new MemoryArray<byte>(_edges.Length);
 6417        var pointers = new MemoryArray<uint>(_pointers.Length);
 6418        var crossEdgePointers = new MemoryArray<uint>(_crossEdgePointers.Length);
 6419        var nextEdgeId = _nextEdgeId;
 6420        var p = 0U;
 6421        var newP = 0U;
 16422        while (p < nextEdgeId)
 10423        {
 424            // read edge data.
 10425            p += this.DecodeVertex(p, out var local1Id, out var tile1Id);
 10426            var vertex1 = new VertexId(tile1Id, local1Id);
 10427            p += this.DecodeVertex(p, out var local2Id, out var tile2Id);
 10428            var vertex2 = new VertexId(tile2Id, local2Id);
 10429            p += this.DecodePointer(p, out _);
 10430            p += this.DecodePointer(p, out _);
 10431            uint? crossEdgeId = null;
 10432            if (tile1Id != tile2Id)
 0433            {
 0434                p += (uint)_edges.GetDynamicUInt32(p, out var c);
 0435                crossEdgeId = c;
 0436            }
 437
 10438            p += (uint)_edges.GetDynamicUInt32Nullable(p, out var _);
 10439            p += (uint)_edges.GetDynamicUInt32Nullable(p, out var length);
 10440            var tailHeadOrder = _edges[p];
 10441            p++;
 10442            p += this.DecodePointer(p, out var shapePointer);
 10443            p += this.DecodePointer(p, out var attributePointer);
 444
 445            // generate new edge type id.
 10446            var newEdgeTypeId = edgeTypeMap.func(this.GetAttributes(attributePointer));
 447
 448            // write edge data again.
 10449            var newEdgePointer = newP;
 10450            newP += EncodeVertex(edges, _tileId, newP, vertex1);
 10451            newP += EncodeVertex(edges, _tileId, newP, vertex2);
 10452            uint? v1p = null;
 10453            if (vertex1.TileId == _tileId)
 10454            {
 10455                v1p = pointers[vertex1.LocalId].DecodeNullableData();
 10456                pointers[vertex1.LocalId] = newEdgePointer.EncodeToNullableData();
 10457            }
 458
 10459            uint? v2p = null;
 10460            if (vertex2.TileId == _tileId)
 10461            {
 10462                v2p = pointers[vertex2.LocalId].DecodeNullableData();
 10463                pointers[vertex2.LocalId] = newEdgePointer.EncodeToNullableData();
 10464            }
 465
 10466            newP += EncodePointer(edges, newP, v1p);
 10467            newP += EncodePointer(edges, newP, v2p);
 10468            if (crossEdgeId != null)
 0469            {
 0470                newP += (uint)edges.SetDynamicUInt32(newP, crossEdgeId.Value);
 0471                if (vertex1.TileId == _tileId)
 0472                {
 0473                    crossEdgePointers[crossEdgeId.Value] = newEdgePointer;
 0474                }
 0475            }
 476
 10477            newP += (uint)edges.SetDynamicUInt32Nullable(newP, newEdgeTypeId);
 10478            newP += (uint)edges.SetDynamicUInt32Nullable(newP, length);
 10479            edges[newP] = tailHeadOrder;
 10480            newP++;
 10481            newP += EncodePointer(edges, newP, shapePointer);
 10482            newP += EncodePointer(edges, newP, attributePointer);
 10483        }
 484
 6485        return new NetworkTile(_zoom, _tileId, edgeTypeMap.id, _nextCrossTileId, pointers, edges, crossEdgePointers,
 6486            _coordinates,
 6487            _shapes, _attributes, _strings, _turnCosts, _nextVertexId, _nextEdgeId,
 6488            _nextAttributePointer, _nextShapePointer, _nextStringId);
 6489    }
 490
 491    internal uint VertexEdgePointer(uint vertex)
 374492    {
 374493        return _pointers[vertex];
 374494    }
 495
 496    internal static uint EncodeVertex(ArrayBase<byte> edges, uint localTileId, uint location, VertexId vertexId)
 568497    {
 568498        if (vertexId.TileId == localTileId)
 555499        {
 500            // same tile, only store local id.
 555501            if (edges.Length <= location + 5)
 173502            {
 173503                edges.Resize(edges.Length + DefaultSizeIncrease);
 173504            }
 505
 555506            return (uint)edges.SetDynamicUInt32(location, vertexId.LocalId);
 507        }
 508
 509        // other tile, store full id.
 13510        if (edges.Length <= location + 10)
 6511        {
 6512            edges.Resize(edges.Length + DefaultSizeIncrease);
 6513        }
 514
 13515        var encodedId = vertexId.Encode();
 13516        return (uint)edges.SetDynamicUInt64(location, encodedId);
 568517    }
 518
 519    internal uint DecodeVertex(uint location, out uint localId, out uint tileId)
 2304520    {
 2304521        var size = (uint)_edges.GetDynamicUInt64(location, out var encodedId);
 2304522        if (encodedId < uint.MaxValue)
 2290523        {
 2290524            localId = (uint)encodedId;
 2290525            tileId = _tileId;
 2290526            return size;
 527        }
 528
 14529        VertexId.Decode(encodedId, out tileId, out localId);
 14530        return size;
 2304531    }
 532
 533    internal uint DecodeEdgeCrossId(uint location, out uint edgeCrossId)
 9534    {
 9535        var s = _edges.GetDynamicUInt32(location, out var c);
 9536        edgeCrossId = EdgeId.MinCrossId + c;
 9537        return (uint)s;
 9538    }
 539
 540    internal uint GetEdgeCrossPointer(uint edgeCrossId)
 3541    {
 3542        return _crossEdgePointers[edgeCrossId];
 3543    }
 544
 545    internal static uint EncodePointer(ArrayBase<byte> edges, uint location, uint? pointer)
 1136546    {
 547        // TODO: save the diff instead of the full pointer.
 1136548        if (edges.Length <= location + 5)
 79549        {
 79550            edges.Resize(edges.Length + DefaultSizeIncrease);
 79551        }
 552
 1136553        return (uint)edges.SetDynamicUInt32(location,
 1136554            pointer.EncodeAsNullableData());
 1136555    }
 556
 557    internal uint DecodePointer(uint location, out uint? pointer)
 4560558    {
 4560559        var size = _edges.GetDynamicUInt32(location, out var data);
 4560560        pointer = data.DecodeNullableData();
 4560561        return (uint)size;
 4560562    }
 563
 564    internal static uint SetDynamicUIn32Nullable(ArrayBase<byte> edges, uint pointer, uint? data)
 540565    {
 540566        while (edges.Length <= pointer + 5)
 0567        {
 0568            edges.Resize(edges.Length + DefaultSizeIncrease);
 0569        }
 570
 540571        return (uint)edges.SetDynamicUInt32Nullable(pointer, data);
 540572    }
 573
 574    internal void GetTailHeadOrder(uint location, ref byte? tail, ref byte? head)
 1127575    {
 1127576        _edges.GetTailHeadOrder(location, ref tail, ref head);
 1127577    }
 578
 579    internal uint DecodeEdgePointerId(uint location, out uint? edgeProfileId)
 2254580    {
 2254581        return (uint)_edges.GetDynamicUInt32Nullable(location, out edgeProfileId);
 2254582    }
 583
 584    private void WriteEdgesAndVerticesTo(Stream stream)
 8585    {
 586        // write vertex pointers.
 8587        stream.WriteVarUInt32(_nextVertexId);
 42588        for (var i = 0; i < _nextVertexId; i++)
 13589        {
 13590            stream.WriteVarUInt32(_pointers[i]);
 13591        }
 592
 593        // write edges.
 8594        stream.WriteVarUInt32(_nextEdgeId);
 106595        for (var i = 0; i < _nextEdgeId; i++)
 45596        {
 45597            stream.WriteByte(_edges[i]);
 45598        }
 599
 600        // write cross edge pointers.
 8601        stream.WriteVarUInt32(_nextCrossTileId);
 16602        for (var i = 0; i < _nextCrossTileId; i++)
 0603        {
 0604            stream.WriteVarUInt32(_crossEdgePointers[i]);
 0605        }
 8606    }
 607
 608    private void ReadEdgesAndVerticesFrom(Stream stream)
 8609    {
 610        // read vertex pointers.
 8611        _nextVertexId = stream.ReadVarUInt32();
 8612        _pointers.Resize(_nextVertexId);
 42613        for (var i = 0; i < _nextVertexId; i++)
 13614        {
 13615            _pointers[i] = stream.ReadVarUInt32();
 13616        }
 617
 618        // read edges.
 8619        _nextEdgeId = stream.ReadVarUInt32();
 8620        _edges.Resize(_nextEdgeId);
 106621        for (var i = 0; i < _nextEdgeId; i++)
 45622        {
 45623            _edges[i] = (byte)stream.ReadByte();
 45624        }
 625
 626        // read cross tile edge pointers.
 8627        _nextCrossTileId = stream.ReadVarUInt32();
 8628        _crossEdgePointers.Resize(_nextCrossTileId);
 16629        for (var i = 0; i < _nextCrossTileId; i++)
 0630        {
 0631            _crossEdgePointers[i] = stream.ReadVarUInt32();
 0632        }
 8633    }
 634}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Geo.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using Itinero.IO;
 5using Itinero.Network.Storage;
 6using Reminiscence.Arrays;
 7
 8namespace Itinero.Network.Tiles;
 9
 10internal partial class NetworkTile
 11{
 12    private const int CoordinateSizeInBytes = 3; // 3 bytes = 24 bits = 4096 x 4096.
 13    private const int TileResolutionInBits = CoordinateSizeInBytes * 8 / 2;
 14    private const int ElevationSizeInBytes = 2; // 2 bytes = 16 bits = [-32768, 32767], using dm as resolution
 15
 16    // the vertex coordinates.
 17    private readonly ArrayBase<byte> _coordinates;
 18    private int? _elevation; // the tile elevation.
 19
 20    // the shapes.
 21021    private uint _nextShapePointer = 0;
 22    private readonly ArrayBase<byte> _shapes;
 23
 24    private void SetCoordinate(uint localId, double longitude, double latitude, float? e)
 43325    {
 26        // set elevation if needed.
 43327        if (_elevation != null && e == null)
 028        {
 029            throw new ArgumentNullException(nameof(e),
 030                "Elevation was set before, either always set elevation or never set elevation.");
 31        }
 32
 43333        if (_elevation == null && e != null)
 1334        {
 1335            if (localId != 0)
 036            {
 037                throw new ArgumentNullException(nameof(e),
 038                    "Elevation was not set before, either always set elevation or never set elevation.");
 39            }
 40
 1341            _elevation = (int)(e * 10);
 1342        }
 43
 44        // make sure coordinates fit.
 45        uint tileCoordinatePointer;
 43346        if (_elevation == null)
 39547        {
 48            // don't store elevation.
 39549            tileCoordinatePointer = localId * CoordinateSizeInBytes * 2;
 39550            _coordinates.EnsureMinimumSize(tileCoordinatePointer + (CoordinateSizeInBytes * 2),
 39551                DefaultSizeIncrease);
 39552        }
 53        else
 3854        {
 55            // store elevation.
 3856            tileCoordinatePointer = localId * ((CoordinateSizeInBytes * 2) + ElevationSizeInBytes);
 3857            _coordinates.EnsureMinimumSize(tileCoordinatePointer + (CoordinateSizeInBytes * 2) + ElevationSizeInBytes,
 3858                DefaultSizeIncrease);
 3859        }
 60
 61        // write coordinates.
 62        const int resolution = (1 << TileResolutionInBits) - 1;
 43363        var (x, y) = TileStatic.ToLocalTileCoordinates(_zoom, _tileId, longitude, latitude, resolution);
 43364        _coordinates.SetFixed(tileCoordinatePointer, CoordinateSizeInBytes, x);
 43365        _coordinates.SetFixed(tileCoordinatePointer + CoordinateSizeInBytes, CoordinateSizeInBytes, y);
 66
 67        // write elevation.
 43368        if (_elevation != null)
 3869        {
 3870            if (e == null)
 071            {
 072                throw new ArgumentNullException(nameof(e),
 073                    "Elevation was set before, either always set elevation or never set elevation.");
 74            }
 75
 3876            var offset = (int)(e.Value * 10) - _elevation.Value;
 3877            _coordinates.SetFixed(tileCoordinatePointer + CoordinateSizeInBytes + CoordinateSizeInBytes,
 3878                ElevationSizeInBytes, offset);
 3879        }
 43380    }
 81
 82    private void GetCoordinate(uint localId, out double longitude, out double latitude, out float? elevation)
 68083    {
 68084        var tileCoordinatePointer = _elevation == null
 68085            ? localId * CoordinateSizeInBytes * 2
 68086            : localId * ((CoordinateSizeInBytes * 2) + ElevationSizeInBytes);
 87
 88        const int resolution = (1 << TileResolutionInBits) - 1;
 68089        _coordinates.GetFixed(tileCoordinatePointer, CoordinateSizeInBytes, out var x);
 68090        _coordinates.GetFixed(tileCoordinatePointer + CoordinateSizeInBytes, CoordinateSizeInBytes, out var y);
 68091        elevation = null;
 68092        if (_elevation != null)
 4793        {
 4794            _coordinates.GetFixed(tileCoordinatePointer + CoordinateSizeInBytes + CoordinateSizeInBytes,
 4795                ElevationSizeInBytes, out var offset);
 4796            elevation = (_elevation.Value + offset) / 10.0f;
 4797        }
 98
 68099        TileStatic.FromLocalTileCoordinates(_zoom, _tileId, x, y, resolution, out longitude, out latitude);
 680100    }
 101
 102    private uint SetShape(IEnumerable<(double longitude, double latitude, float? e)> shape)
 88103    {
 104        const int resolution = (1 << TileResolutionInBits) - 1;
 88105        var originalPointer = _nextShapePointer;
 88106        var blockPointer = originalPointer;
 88107        var pointer = blockPointer + 1;
 108
 109        // make sure there is space for the block pointer.
 88110        _shapes.EnsureMinimumSize(blockPointer);
 111
 88112        var coordinateBlockSize = 8;
 88113        if (_elevation != null)
 24114        {
 24115            coordinateBlockSize += 4;
 24116        }
 117
 88118        using var enumerator = shape.GetEnumerator();
 88119        var count = 0;
 88120        (int x, int y, int? eOffset) previous = (int.MaxValue, int.MaxValue, null);
 443121        while (enumerator.MoveNext())
 355122        {
 355123            var current = enumerator.Current;
 355124            var (x, y) =
 355125                TileStatic.ToLocalTileCoordinates(_zoom, _tileId, current.longitude, current.latitude, resolution);
 355126            int? eOffset = null;
 355127            var e = current.e ?? 0;
 355128            if (_elevation != null)
 15129            {
 15130                eOffset = (int)(e * 10) - _elevation.Value;
 15131            }
 132
 133            // make sure there is space for this coordinate.
 355134            _shapes.EnsureMinimumSize(pointer + coordinateBlockSize);
 135
 136            // store coordinate.
 355137            if (count == 0)
 48138            {
 139                // first coordinate.
 48140                pointer += (uint)_shapes.SetDynamicInt32(pointer, x);
 48141                pointer += (uint)_shapes.SetDynamicInt32(pointer, y);
 48142                if (eOffset != null)
 7143                {
 7144                    pointer += (uint)_shapes.SetDynamicInt32(pointer, eOffset.Value);
 7145                }
 48146            }
 147            else
 307148            {
 149                // calculate diff and then store.
 307150                var diffX = x - previous.x;
 307151                var diffY = y - previous.y;
 307152                pointer += (uint)_shapes.SetDynamicInt32(pointer, diffX);
 307153                pointer += (uint)_shapes.SetDynamicInt32(pointer, diffY);
 307154                if (eOffset != null)
 8155                {
 8156                    if (previous.eOffset == null)
 0157                    {
 0158                        throw new ArgumentException("Not all points have elevation set.");
 159                    }
 160
 8161                    var diffE = eOffset.Value - previous.eOffset.Value;
 8162                    pointer += (uint)_shapes.SetDynamicInt32(pointer, diffE);
 8163                }
 307164            }
 165
 355166            count++;
 167
 355168            if (count == 255)
 1169            {
 170                // start a new block, assign 255.
 1171                _shapes[blockPointer] = 255;
 1172                blockPointer = pointer;
 1173                pointer = blockPointer + 1;
 1174                count = 0;
 1175            }
 176
 355177            previous = (x, y, eOffset);
 355178        }
 179
 180        // a block is still open, close it.
 88181        _shapes[blockPointer] = (byte)count;
 88182        _nextShapePointer = pointer;
 183
 88184        return originalPointer;
 88185    }
 186
 187    internal IEnumerable<(double longitude, double latitude, float? e)> GetShape(uint? pointer)
 365188    {
 365189        if (pointer == null)
 192190        {
 192191            yield break;
 192        }
 193
 173194        var p = pointer.Value;
 195
 196        const int resolution = (1 << TileResolutionInBits) - 1;
 173197        var count = -1;
 173198        (int x, int y, int? eOffset) previous = (int.MaxValue, int.MaxValue, null);
 174199        while (true)
 174200        {
 174201            count = _shapes[p];
 174202            p++;
 203
 1276204            for (var i = 0; i < count; i++)
 466205            {
 466206                p += (uint)_shapes.GetDynamicInt32(p, out var x);
 466207                p += (uint)_shapes.GetDynamicInt32(p, out var y);
 466208                int? eOffset = null;
 466209                if (_elevation != null)
 24210                {
 24211                    p += (uint)_shapes.GetDynamicInt32(p, out var e);
 24212                    eOffset = e;
 24213                }
 214
 466215                if (i > 0)
 352216                {
 352217                    x = previous.x + x;
 352218                    y = previous.y + y;
 352219                    if (_elevation != null)
 12220                    {
 12221                        if (previous.eOffset == null)
 0222                        {
 0223                            throw new ArgumentException("Not all points have elevation set.");
 224                        }
 225
 12226                        eOffset = previous.eOffset.Value + eOffset;
 12227                    }
 352228                }
 229
 466230                int? elevation = null;
 466231                if (_elevation != null)
 24232                {
 24233                    if (eOffset == null)
 0234                    {
 0235                        throw new ArgumentException("Not all points have elevation set.");
 236                    }
 237
 24238                    elevation = _elevation.Value + eOffset.Value;
 24239                }
 240
 466241                TileStatic.FromLocalTileCoordinates(_zoom, _tileId, x, y, resolution, out var longitude,
 466242                    out var latitude);
 466243                yield return (longitude, latitude, elevation / 10.0f);
 244
 464245                previous = (x, y, eOffset);
 464246            }
 247
 343248            if (count < 255) break;
 1249        }
 171250    }
 251
 252    private void WriteGeoTo(Stream stream)
 8253    {
 8254        stream.WriteVarInt32Nullable(_elevation);
 255
 256        // write vertex locations.
 8257        var coordinateSize = CoordinateSizeInBytes * 2;
 8258        if (_elevation != null)
 2259        {
 2260            coordinateSize += ElevationSizeInBytes;
 2261        }
 262
 8263        var coordinateBytes = _nextVertexId * coordinateSize;
 184264        for (var i = 0; i < coordinateBytes; i++)
 84265        {
 84266            stream.WriteByte(_coordinates[i]);
 84267        }
 268
 269        // write shape locations.
 8270        stream.WriteVarUInt32(_nextShapePointer);
 74271        for (var i = 0; i < _nextShapePointer; i++)
 29272        {
 29273            stream.WriteByte(_shapes[i]);
 29274        }
 8275    }
 276
 277    private void ReadGeoFrom(Stream stream)
 8278    {
 8279        _elevation = stream.ReadVarInt32Nullable();
 280
 281        // read vertex locations.
 8282        var coordinateSize = CoordinateSizeInBytes * 2;
 8283        if (_elevation != null)
 2284        {
 2285            coordinateSize += ElevationSizeInBytes;
 2286        }
 287
 8288        var coordinateBytes = _nextVertexId * coordinateSize;
 8289        _coordinates.Resize(coordinateBytes);
 184290        for (var i = 0; i < coordinateBytes; i++)
 84291        {
 84292            _coordinates[i] = (byte)stream.ReadByte();
 84293        }
 294
 8295        _nextShapePointer = stream.ReadVarUInt32();
 8296        _shapes.Resize(_nextShapePointer);
 74297        for (var i = 0; i < _nextShapePointer; i++)
 29298        {
 29299            _shapes[i] = (byte)stream.ReadByte();
 29300        }
 8301    }
 302}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Serialization.cs

#LineLine coverage
 1using System.IO;
 2using Itinero.IO;
 3
 4namespace Itinero.Network.Tiles;
 5
 6internal partial class NetworkTile
 7{
 8    public void WriteTo(Stream stream)
 89    {
 10        const int version = 1;
 811        stream.WriteVarInt32(version);
 12
 13        // write tile id/zoom.
 814        stream.WriteVarInt32(_zoom);
 815        stream.WriteVarUInt32(_tileId);
 816        stream.WriteGuid(_edgeTypeMapId);
 17
 18        // write vertices and edges.
 819        this.WriteEdgesAndVerticesTo(stream);
 20
 21        // write attributes.
 822        this.WriteAttributesTo(stream);
 23
 24        // write shapes.
 825        this.WriteGeoTo(stream);
 26
 27        // write turn costs.
 828        this.WriteTurnCostsTo(stream);
 829    }
 30
 31    public static NetworkTile ReadFrom(Stream stream)
 832    {
 833        var version = stream.ReadVarInt32();
 834        if (version != 1)
 035        {
 036            throw new InvalidDataException("Cannot deserialize tiles: Invalid version #.");
 37        }
 38
 39        // read tile id.
 840        var zoom = stream.ReadVarInt32();
 841        var tileId = stream.ReadVarUInt32();
 842        var edgeTypeMapId = stream.ReadGuid();
 43
 44        // create the tile.
 845        var graphTile = new NetworkTile(zoom, tileId, edgeTypeMapId);
 46
 47        // read vertices and edges.
 848        graphTile.ReadEdgesAndVerticesFrom(stream);
 49
 50        // read attributes.
 851        graphTile.ReadAttributesFrom(stream);
 52
 53        // read shapes.
 854        graphTile.ReadGeoFrom(stream);
 55
 56        // read turn costs.
 857        graphTile.ReadTurnCostsFrom(stream);
 58
 859        return graphTile;
 860    }
 61}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.TurnCosts.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Linq;
 5using Itinero.IO;
 6using Itinero.Network.Storage;
 7using Itinero.Network.TurnCosts;
 8using Reminiscence.Arrays;
 9
 10namespace Itinero.Network.Tiles;
 11
 12internal partial class NetworkTile
 13{
 21014    private uint _turnCostPointer = 0;
 21015    private readonly ArrayBase<uint> _turnCostPointers = new MemoryArray<uint>(0);
 21016    private readonly ArrayBase<byte> _turnCosts = new MemoryArray<byte>(0);
 17
 18    internal void AddTurnCosts(VertexId vertex, uint turnCostType,
 19        EdgeId[] edges, uint[,] costs, IEnumerable<(string key, string value)> attributes,
 20        IEnumerable<EdgeId>? prefix = null)
 1321    {
 1322        prefix ??= ArraySegment<EdgeId>.Empty;
 23
 1324        if (edges.Length > OrderCoder.MaxOrderHeadTail)
 025        {
 026            throw new ArgumentException(
 027                $"Cannot add turn costs for vertices with more than {OrderCoder.MaxOrderHeadTail} edges.");
 28        }
 29
 30        // enumerate the edges associated with the vertex.
 1331        var enumerator = new NetworkTileEnumerator();
 1332        enumerator.MoveTo(this);
 1333        if (!enumerator.MoveTo(vertex))
 034        {
 035            throw new ArgumentException($"Cannot add turn costs to a vertex that doesn't exist.",
 036                nameof(vertex));
 37        }
 38
 39        // determine max existing order, perhaps there are other turn costs already.
 1340        var orders = new byte?[edges.Length];
 1341        var max = -1;
 3942        while (enumerator.MoveNext())
 2643        {
 4844            if (!enumerator.TailOrder.HasValue) continue;
 445            if (enumerator.TailOrder.Value <= max) continue;
 46
 447            max = enumerator.TailOrder.Value;
 448        }
 49
 50        // assign missing orders if any.
 1351        enumerator.Reset();
 1352        var next = max + 1;
 3953        while (enumerator.MoveNext())
 2654        {
 55            // only assign orders of edges that are used.
 2656            var i = Array.IndexOf(edges, enumerator.EdgeId);
 2657            if (i == -1) continue;
 58
 59            // edge is used, assign or reuse existing.
 2660            var order = enumerator.TailOrder;
 2661            if (order == null)
 2262            {
 63                // get next order.
 2264                order = (byte)next;
 2265                next++;
 66
 67                // set order, it's different.
 2268                if (enumerator.Forward)
 1169                {
 70                    // if the edge is forward the tail in the enumerator is also the tail in the edge.
 1171                    this.SetTailHeadOrder(enumerator.EdgePointer, order, enumerator.HeadOrder);
 1172                }
 73                else
 1174                {
 75                    // if the edge is backward the tail in the enumerator is head in the edge.
 1176                    this.SetTailHeadOrder(enumerator.EdgePointer, enumerator.TailOrder, order);
 1177                }
 2278            }
 79
 2680            orders[i] = order;
 2681        }
 82
 83        // reversed order array.
 1384        var count = next;
 1385        var reversedOrders = new byte?[count];
 7886        for (var i = 0; i < orders.Length; i++)
 2687        {
 2688            var order = orders[i];
 2689            if (order == null)
 090            {
 091                continue;
 92            }
 93
 2694            reversedOrders[order.Value] = (byte)i;
 2695        }
 96
 97        // make sure there is space in the pointers table.
 98        // and initialize new slots with null.
 2499        while (_turnCostPointers.Length <= vertex.LocalId)
 11100        {
 11101            _turnCostPointers.Resize(_nextVertexId);
 11102        }
 103
 104        // make sure there is space in the turn cost array.
 105        // and initialize new slots with null.
 13106        var maxLength = _turnCostPointer + 5 + 1 + (count * count * 5) + 5;
 37107        while (_turnCosts.Length <= maxLength)
 24108        {
 24109            _turnCosts.Resize(_turnCosts.Length + DefaultSizeIncrease);
 24110        }
 111
 112        // update pointer to reflect new data.
 13113        var previousPointer = _turnCostPointers[vertex.LocalId].DecodeNullableData();
 13114        _turnCostPointers[vertex.LocalId] = _turnCostPointer.EncodeToNullableData();
 115
 116        // write turn cost types.
 13117        _turnCostPointer += (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, turnCostType);
 118
 119        // write attributes.
 13120        var a = this.SetAttributes(attributes);
 13121        _turnCostPointer += (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, a);
 122
 123        // write prefix sequence.
 13124        var prefixEdges = new List<EdgeId>(prefix);
 13125        _turnCostPointer += (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, (uint)prefixEdges.Count);
 39126        foreach (var prefixEdge in prefixEdges)
 0127        {
 0128            if (prefixEdge.TileId == _tileId)
 0129            {
 0130                _turnCostPointer += (uint)_turnCosts.SetDynamicInt32(_turnCostPointer, (int)prefixEdge.LocalId);
 0131            }
 132            else
 0133            {
 0134                _turnCostPointer += (uint)_turnCosts.SetDynamicInt32(_turnCostPointer, (int)-(prefixEdge.LocalId + 1));
 0135                _turnCostPointer += (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, prefixEdge.TileId);
 0136            }
 0137        }
 138
 139        // write turn costs.
 13140        _turnCosts[_turnCostPointer] = (byte)count;
 13141        _turnCostPointer++;
 78142        for (var x = 0; x < count; x++)
 26143        {
 26144            var xOrder = reversedOrders[x];
 156145            for (var y = 0; y < count; y++)
 52146            {
 52147                var yOrder = reversedOrders[y];
 148
 149                // get cost from original matrix.
 52150                uint cost = 0;
 52151                if (xOrder != null && yOrder != null)
 52152                {
 52153                    cost = costs[xOrder.Value, yOrder.Value];
 52154                }
 155
 156                // write cost.
 52157                _turnCostPointer += (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, cost);
 52158            }
 26159        }
 160
 161        // write previous turn cost pointer at the end.
 13162        _turnCostPointer +=
 13163            (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, previousPointer.EncodeAsNullableData());
 13164    }
 165
 166    internal IEnumerable<(uint turnCostType, IEnumerable<(string key, string value)> attributes, uint cost,
 167            IEnumerable<EdgeId> prefixEdges)>
 168        GetTurnCosts(VertexId vertex, byte fromOrder, byte toOrder)
 21169    {
 21170        if (_turnCostPointers.Length <= vertex.LocalId)
 0171        {
 0172            yield break;
 173        }
 174
 21175        var pointerNullable = _turnCostPointers[vertex.LocalId].DecodeNullableData();
 21176        if (pointerNullable == null)
 0177        {
 0178            yield break;
 179        }
 180
 21181        var pointer = pointerNullable.Value;
 25182        while (true)
 25183        {
 184            // return turn cost type.
 25185            pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var turnCostType);
 186
 187            // read attributes.
 25188            pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var a);
 25189            var attributes = this.GetAttributes(a);
 190
 191            // read prefix edges.
 25192            IEnumerable<EdgeId> prefixEdges = ArraySegment<EdgeId>.Empty;
 25193            pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var prefixEdgeCount);
 25194            if (prefixEdgeCount > 0)
 0195            {
 0196                var prefixEdgesList = new List<EdgeId>();
 0197                while (prefixEdgeCount > 0)
 0198                {
 0199                    pointer += (uint)_turnCosts.GetDynamicInt32(pointer, out var signedLocalId);
 0200                    if (signedLocalId >= 0)
 0201                    {
 0202                        prefixEdgesList.Add(new EdgeId(this.TileId, (uint)signedLocalId));
 0203                    }
 204                    else
 0205                    {
 0206                        pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var tileId);
 0207                        prefixEdgesList.Add(new EdgeId(tileId, (uint)-signedLocalId - 1));
 0208                    }
 209
 0210                    prefixEdgeCount--;
 0211                }
 212
 0213                prefixEdges = prefixEdgesList;
 0214            }
 215
 216            // read turn cost table.
 25217            var max = _turnCosts[pointer];
 25218            pointer++;
 219
 126220            for (var x = 0; x < max; x++)
 46221            {
 252222                for (var y = 0; y < max; y++)
 88223                {
 88224                    pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var cost);
 88225                    if (fromOrder != x || toOrder != y)
 63226                    {
 63227                        continue;
 228                    }
 229
 25230                    if (cost != 0)
 23231                    {
 23232                        yield return (turnCostType, attributes, cost, prefixEdges);
 15233                    }
 17234                }
 38235            }
 236
 237            // get pointer to next turn cost table.
 17238            _turnCosts.GetDynamicUInt32(pointer, out var p);
 17239            pointerNullable = p.DecodeNullableData();
 17240            if (pointerNullable == null)
 13241            {
 13242                break;
 243            }
 244
 4245            pointer = pointerNullable.Value;
 4246        }
 13247    }
 248
 249    private void SetTailHeadOrder(uint pointer, byte? tailOrder, byte? headOrder)
 22250    {
 251        // skip over vertices and next-pointers.
 22252        var size = this.DecodeVertex(pointer, out _, out var t1);
 22253        pointer += size;
 22254        size = this.DecodeVertex(pointer, out _, out var t2);
 22255        pointer += size;
 22256        size = this.DecodePointer(pointer, out _);
 22257        pointer += size;
 22258        size = this.DecodePointer(pointer, out _);
 22259        pointer += size;
 260
 261        // skip edge id if needed.
 22262        if (t1 != t2)
 0263        {
 0264            size = (uint)_edges.GetDynamicUInt32(pointer, out _);
 0265            pointer += size;
 0266        }
 267
 268        // skip over length/type.
 22269        size = this.DecodeEdgePointerId(pointer, out _);
 22270        pointer += size;
 22271        size = this.DecodeEdgePointerId(pointer, out _);
 22272        pointer += size;
 273
 274        // get existing head/tail order.
 22275        byte? existingTailOrder = null;
 22276        byte? existingHeadOrder = null;
 22277        this.GetTailHeadOrder(pointer, ref existingTailOrder, ref existingHeadOrder);
 278
 279        // set tail order if there is a value.
 22280        if (tailOrder.HasValue)
 11281        {
 11282            if (existingTailOrder.HasValue && existingTailOrder.Value != tailOrder.Value)
 0283                throw new InvalidOperationException("An edge tail or head order can only be set once.");
 11284            existingTailOrder = tailOrder;
 11285        }
 286
 287        // set head order if there is a value.
 22288        if (headOrder.HasValue)
 11289        {
 11290            if (existingHeadOrder.HasValue && existingHeadOrder.Value != headOrder.Value)
 0291                throw new InvalidOperationException("An edge tail or head order can only be set once.");
 11292            existingHeadOrder = headOrder;
 11293        }
 294
 22295        _edges.SetTailHeadOrder(pointer, existingTailOrder, existingHeadOrder);
 22296    }
 297
 298    private void WriteTurnCostsTo(Stream stream)
 8299    {
 8300        stream.WriteVarUInt32((uint)_turnCostPointers.Length);
 16301        for (var i = 0; i < _turnCostPointers.Length; i++)
 0302        {
 0303            stream.WriteVarUInt32(_turnCostPointers[i]);
 0304        }
 305
 8306        stream.WriteVarUInt32(_turnCostPointer);
 16307        for (var i = 0; i < _turnCostPointer; i++)
 0308        {
 0309            stream.WriteByte(_turnCosts[i]);
 0310        }
 8311    }
 312
 313    private void ReadTurnCostsFrom(Stream stream)
 8314    {
 8315        var turnCostPointersSize = stream.ReadVarUInt32();
 8316        _turnCostPointers.Resize(turnCostPointersSize);
 16317        for (var i = 0; i < turnCostPointersSize; i++)
 0318        {
 0319            _turnCostPointers[i] = stream.ReadVarUInt32();
 0320        }
 321
 8322        _turnCostPointer = stream.ReadVarUInt32();
 8323        _turnCosts.Resize(_turnCostPointer);
 16324        for (var i = 0; i < _turnCostPointer; i++)
 0325        {
 0326            _turnCosts[i] = (byte)stream.ReadByte();
 0327        }
 8328    }
 329}