idbag

Check-in Differences
Login

Check-in Differences

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From idbag-0.1.2 To idbag-0.2.0

2024-10-02
16:42
Update changelog. Use hashbrown 0.15.0. check-in: 4d7c70b176 user: jan tags: trunk
2024-09-20
04:10
Fixups. check-in: e7b22a4490 user: jan tags: idbag-0.2.0, trunk
03:56
Merge macroification. check-in: d26d429dd3 user: jan tags: trunk
2024-09-10
03:49
Crate maintenance. Add bacon conf, but don't include it in packaging. check-in: 373e9cdf32 user: jan tags: trunk
2024-01-23
09:51
Crate maintenance. Remove dev-docs feature. check-in: 25207ac9a2 user: jan tags: idbag-0.1.2, trunk
2023-08-30
17:04
Prepare for publish of 0.1.1. check-in: df0812d15e user: jan tags: idbag-0.1.1, trunk

Changes to Cargo.toml.

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






[package]
name = "idbag"
version = "0.1.2"
edition = "2021"
license = "0BSD"

categories = [ "data-structures" ]
keywords = [ "bag", "identifier" ]
repository = "https://repos.qrnch.tech/pub/idbag"
description = "A bag of u32 identifiers."
rust-version = "1.56"
exclude = [
  ".fossil-settings",
  ".efiles",
  ".fslckout",
  "www",

  "rustfmt.toml"
]





[dependencies]
hashbrown = { version = "0.14.3" }
parking_lot = { version = "0.12.1" }


[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]









|


>



|
|





>



>
>
>
>

|
|
>




>
>
>
>
>
>
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
[package]
name = "idbag"
version = "0.2.0"
edition = "2021"
license = "0BSD"
# https://crates.io/category_slugs
categories = [ "data-structures" ]
keywords = [ "bag", "identifier" ]
repository = "https://repos.qrnch.tech/pub/idbag"
description = "A bag of integers."
rust-version = "1.70"
exclude = [
  ".fossil-settings",
  ".efiles",
  ".fslckout",
  "www",
  "bacon.toml",
  "rustfmt.toml"
]

# https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section
[badges]
maintenance = { status = "passively-maintained" }

[dependencies]
hashbrown = { version = "0.14.5" }
parking_lot = { version = "0.12.3" }
paste = { version = "1.0.15" }

[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]

[lints.clippy]
all = { level = "deny", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }

Changes to README.md.

1
2
3
4
5
# idbag

A bag that initially contains (all) `u32` values that can be allocated and
returned to the bag.



<
|

1
2

3
4
# idbag


A bag of integers that can be allocated and then returned to the bag.

Added bacon.toml.



















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
100
101
102
103
104
105
# This is a configuration file for the bacon tool
#
# Bacon repository: https://github.com/Canop/bacon
# Complete help on configuration: https://dystroy.org/bacon/config/
# You can also check bacon's own bacon.toml file
#  as an example: https://github.com/Canop/bacon/blob/main/bacon.toml

# For information about clippy lints, see:
# https://github.com/rust-lang/rust-clippy/blob/master/README.md

#default_job = "check"
default_job = "clippy-all"

[jobs.check]
command = ["cargo", "check", "--color", "always"]
need_stdout = false

[jobs.check-all]
command = ["cargo", "check", "--all-targets", "--color", "always"]
need_stdout = false

# Run clippy on the default target
[jobs.clippy]
command = [
    "cargo", "clippy",
    "--color", "always",
]
need_stdout = false

# Run clippy on all targets
# To disable some lints, you may change the job this way:
#    [jobs.clippy-all]
#    command = [
#        "cargo", "clippy",
#        "--all-targets",
#        "--color", "always",
#    	 "--",
#    	 "-A", "clippy::bool_to_int_with_if",
#    	 "-A", "clippy::collapsible_if",
#    	 "-A", "clippy::derive_partial_eq_without_eq",
#    ]
# need_stdout = false
[jobs.clippy-all]
command = [
    "cargo", "clippy",
    "--all-targets",
    "--color", "always",
]
need_stdout = false

# This job lets you run
# - all tests: bacon test
# - a specific test: bacon test -- config::test_default_files
# - the tests of a package: bacon test -- -- -p config
[jobs.test]
command = [
    "cargo", "test", "--color", "always",
    "--", "--color", "always", # see https://github.com/Canop/bacon/issues/124
]
need_stdout = true

[jobs.doc]
command = ["cargo", "doc", "--color", "always", "--no-deps"]
need_stdout = false

# If the doc compiles, then it opens in your browser and bacon switches
# to the previous job
[jobs.doc-open]
command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"]
need_stdout = false
on_success = "back" # so that we don't open the browser at each change

# You can run your application and have the result displayed in bacon,
# *if* it makes sense for this crate.
# Don't forget the `--color always` part or the errors won't be
# properly parsed.
# If your program never stops (eg a server), you may set `background`
# to false to have the cargo run output immediately displayed instead
# of waiting for program's end.
[jobs.run]
command = [
    "cargo", "run",
    "--color", "always",
    # put launch parameters for your program behind a `--` separator
]
need_stdout = true
allow_warnings = true
background = true

# This parameterized job runs the example of your choice, as soon
# as the code compiles.
# Call it as
#    bacon ex -- my-example
[jobs.ex]
command = ["cargo", "run", "--color", "always", "--example"]
need_stdout = true
allow_warnings = true

# You may define here keybindings that would be specific to
# a project, for example a shortcut to launch a specific job.
# Shortcuts to internal functions (scrolling, toggling, etc.)
# should go in your personal global prefs.toml file instead.
[keybindings]
# alt-m = "job:my-job"
c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target

Changes to src/lib.rs.

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
100
101
102
103


104
105

106
107
108
109
110
111
112

113
114
115
116
117
118


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133


134
135
136
137
138
139

140
141
142
143
144
145
146
147
148




149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

178
179
180


181
182
183
184
185
186
187


188
189
190
191







192







193
194
195
196
197
198

199


200
201
202
203
204
205
206
207




208
209
210

211
212

213
214

215
216
217
218
219
220
221
222
223
224
225

226


227
228
229
230

231






232
233

234
235

236



237
238
239
240
241
242
243
244
245
246









247
248
//! The [`IdBag`] is conceptually a bag that initially contains (all) `u32`
//! values.  An application can request to allocate an [`Id`] that will contain

//! a `u32` value taken from the bag.  Once the Id is dropped its `u32` will
//! automatically be returned to the bag and be available for allocation again.
//!
//! `Id` objects are hashable and transparently maps to its internal `u32`.
//! They can also be
//!
//! # Introductionary example
//! ```
//! use idbag::IdBag;
//!
//! // Create a new id heap.
//! let idbag = IdBag::new();
//!
//! // Allocate an id; first id will have a value of 1.
//! let id = idbag.alloc();
//! assert_eq!(id.val(), 1);
//!
//! // The id's drop handler returns 1 to the idbag
//! drop(id);
//!
//! // Allocate another id; next id will have a value of 2.
//! let id = idbag.alloc();
//! assert_eq!(id.val(), 2);
//!
//! // The id's drop handler returns 2 to the idbag
//! drop(id);
//! ```








use std::{
  borrow::Borrow,
  fmt,
  hash::{Hash, Hasher},
  sync::{Arc, Weak}
};

use parking_lot::Mutex;

use hashbrown::HashSet;







/// Internal representation of the IdBag.
#[derive(Default)]
pub struct InnerBag {
  set: HashSet<u32>,
  idgen: u32
}

/// A collection of all `u32` integers that can be allocated.


#[derive(Default)]
#[repr(transparent)]
pub struct IdBag(Arc<Mutex<InnerBag>>);

impl IdBag {
  /// Create a new id bag.
  pub fn new() -> Self {
    Self::default()
  }









  /// Create a new id generator using a pre-existing set.
  ///
  /// This is useful when multiple `IdBag` instances should share the
  /// same inner context.  The practical implication of this is that the
  /// `IdBag`'s that share the same inner context will not yield overlapping
  /// identifier values.
  ///
  /// ```
  /// use idbag::IdBag;
  ///
  /// let idheap1 = IdBag::new();
  /// let idheap2 = IdBag::with_inner(idheap1.clone_inner());
  ///
  /// let id1 = idheap1.alloc();
  /// assert_eq!(id1.val(), 1);
  ///
  /// let id2 = idheap2.alloc();
  /// assert_eq!(id2.val(), 2);
  /// ```
  pub fn with_inner(inner: Arc<Mutex<InnerBag>>) -> Self {
    Self(inner)
  }













  /// Return a clone of the internal id collection.

  pub fn clone_inner(&self) -> Arc<Mutex<InnerBag>> {
    Arc::clone(&self.0)
  }



  /// Allocate a new identifier.
  ///
  /// ```
  /// use idbag::IdBag;
  ///
  /// let idheap = IdBag::new();
  ///
  /// let id = idheap.alloc();
  /// assert_eq!(id.val(), 1);
  ///
  /// let id = idheap.alloc();
  /// assert_eq!(id.val(), 2);
  /// ```
  ///
  /// # Desgin flaws
  /// This function will hang if all the id's have been exhausted.


  pub fn alloc(&self) -> Id {
    let mut g = self.0.lock();

    let id = loop {
      g.idgen = g.idgen.wrapping_add(1);
      if !g.set.contains(&g.idgen) {
        break g.idgen;
      }
    };
    g.set.insert(id);

    Id {
      id,
      idbag: Arc::downgrade(&self.0)
    }
  }



  /// Allocate a specific id, if available.
  ///
  /// ```
  /// use idbag::IdBag;
  ///
  /// let idheap = IdBag::new();
  ///
  /// let Some(id) = idheap.alloc_id(42) else {
  ///   panic!("Unexpectedly unable to allocate id 42");
  /// };
  /// assert_eq!(id.val(), 42);
  ///
  /// let id = idheap.alloc_id(42);
  /// assert_eq!(id, None);
  /// ```


  pub fn alloc_id(&self, id: u32) -> Option<Id> {
    let mut g = self.0.lock();
    if g.set.contains(&id) {
      None
    } else {
      g.set.insert(id);

      Some(Id {
        id,
        idbag: Arc::downgrade(&self.0)
      })
    }
  }
}

/// Representation of an allocated identifier.




pub struct Id {
  id: u32,
  idbag: Weak<Mutex<InnerBag>>
}

impl fmt::Debug for Id {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Id:{}", self.id)
  }
}

impl Hash for Id {
  fn hash<H>(&self, state: &mut H)
  where
    H: Hasher
  {
    self.id.hash(state);
  }
}

impl PartialEq for Id {
  fn eq(&self, other: &Self) -> bool {
    self.id == other.id
  }
}

impl Eq for Id {}

impl Borrow<u32> for Id {

  /// Allow Id to be borrowed as a `&u32`.
  ///
  /// This allows a `HashMap<Id, T>` to look up entries using a `&u32`.


  fn borrow(&self) -> &u32 {
    &self.id
  }
}

impl Id {
  /// Return the internal identifier `u32` value.


  pub fn val(&self) -> u32 {
    self.id
  }








  /// Turn object into an [`ArcId`].







  pub fn into_arcid(self) -> ArcId {
    ArcId(Arc::new(self))
  }
}

impl Drop for Id {

  /// When an [`Id`] is dropped, return the internal integer to the collection.


  fn drop(&mut self) {
    if let Some(idbag) = self.idbag.upgrade() {
      let mut g = idbag.lock();
      g.set.remove(&self.id);
    }
  }
}





/// An atomically referenced counted version of [`Id`].
///
/// This exists primarily because we want to be able to look up `Arc<Id>`'s by

/// `u32`'s in `HashMap`'s.  While it is possible to look up `Id`'s in
/// `HashMap`'s using `&u32`'s, it doesn't work for `Arc<Id>`'s.  To workaround

/// we create an `ArcId` newtype that implements `Borrow<u32>`, which passes it
/// through the `Arc` to it's inner `Id`.

#[derive(Clone, Hash, PartialEq, Eq)]
#[repr(transparent)]
pub struct ArcId(Arc<Id>);

impl fmt::Debug for ArcId {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Id:{}", self.0.id)
  }
}

impl ArcId {

  /// Return the internal identifier `u32` value.


  pub fn val(&self) -> u32 {
    self.0.val()
  }


  /// Returns the inner value, if the `ArcId` has exactly one strong reference.






  pub fn try_unwrap(this: ArcId) -> Result<Id, ArcId> {
    Arc::<Id>::try_unwrap(this.0).map_err(ArcId)

  }


  /// Returns the inner value, if the `ArcId` has exactly one strong reference.



  pub fn into_inner(this: ArcId) -> Option<Id> {
    Arc::<Id>::into_inner(this.0)
  }
}

impl Borrow<u32> for ArcId {
  fn borrow(&self) -> &u32 {
    (*self.0).borrow()
  }
}










// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
|
|
>
|
|

|
|



|

|
|















>
>
>
>
>
>
>












>
>
>
>
>
>
|
|
|
|
|
|
|
|
>
>
|
|
|
|
<
<
<
<
|
>
>
>
>
>
>
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
|
>
>

>
>
>
>
>
>
>
>
>
>
|
>
|
|
|

>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|
>
>
|
|
>
|
|
|
|
|
|
|
>
|
|
|
|
|

>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
>
>
>
>
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|
|
|

|
|
|
|
|

|

|
>
|
|
|
>
>
|
|
|
|

|
|
>
>
|
|
|

>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
|
|
|
|

|
>
|
>
>
|
|
|
|
|
|
|
|
>
>
>
>
|
|
|
>
|
|
>
|
|
>
|
|
|

|
|
|
|
|

|
>
|
>
>
|
|
|

>
|
>
>
>
>
>
>
|
|
>
|

>
|
>
>
>
|
|
|
|

|
|
|
|
|
>
>
>
>
>
>
>
>
>


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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
//! An id bag is conceptually a bag that initially contains (all) intger
//! values of the type of the bag.  An application can request to allocate an
//! id from the bag, which behaves like the identifier has been taken out of
//! the bag.  Once the id is dropped its integer will automatically be
//! returned to the bag and be available for allocation again.
//!
//! Allocated identifier objects are hashable and derefernces to its internal
//! integer value.
//!
//! # Introductionary example
//! ```
//! use idbag::IdBagU32;
//!
//! // Create a new id bag.
//! let idbag = IdBagU32::new();
//!
//! // Allocate an id; first id will have a value of 1.
//! let id = idbag.alloc();
//! assert_eq!(id.val(), 1);
//!
//! // The id's drop handler returns 1 to the idbag
//! drop(id);
//!
//! // Allocate another id; next id will have a value of 2.
//! let id = idbag.alloc();
//! assert_eq!(id.val(), 2);
//!
//! // The id's drop handler returns 2 to the idbag
//! drop(id);
//! ```
//!
//! # Assigned integer semantics
//! The assigned integer increases by one each time a new id has been allocated
//! and wraps around at its maximum value.

#![forbid(missing_docs)]
#![forbid(clippy::missing_docs_in_private_items)]

use std::{
  borrow::Borrow,
  fmt,
  hash::{Hash, Hasher},
  sync::{Arc, Weak}
};

use parking_lot::Mutex;

use hashbrown::HashSet;

use paste::paste;

/// Implement an id bag for an integer type.
macro_rules! impl_Bag {
  ($int_type:ident $int_name:ident) => {
    paste! {
      #[doc = "Internal representation of the  [`IdBag" $int_name "`]."]
      #[derive(Default)]
      pub struct [<InnerBag $int_name>] {
        set: HashSet<$int_type>,
        idgen: $int_type
      }
    }

    paste! {
      #[doc = "A collection of allocatable `" $int_type "` integers."]
      #[derive(Default)]
      #[repr(transparent)]
      pub struct [<IdBag $int_name>](Arc<Mutex<[<InnerBag $int_name>]>>);
    }





    paste! {
      impl [<IdBag $int_name>] {
        #[doc = "Create a new id bag containing `" $int_type "` integers."]
        #[must_use]
        pub fn new() -> Self {
          Self::default()
        }

        #[doc = r#"
        Create a new id generator using a pre-existing set.

        This is useful when multiple `IdBag` instances should share the
        same inner context.  The practical implication of this is that the
        `IdBag`'s that share the same inner context will not yield overlapping
        identifier values.

        ```
        use idbag::IdBag"# $int_name r#";

        let idbag1 = IdBag"# $int_name r#"::new();
        let idbag2 = IdBag"# $int_name r#"::with_inner(idbag1.clone_inner());










        let id1 = idbag1.alloc();
        assert_eq!(id1.val(), 1);

        let id2 = idbag2.alloc();
        assert_eq!(id2.val(), 2);
        ```
        "#]
        pub const fn with_inner(
          inner: Arc<Mutex<[<InnerBag $int_name>]>>
        ) -> Self {
          Self(inner)
        }

        /// Return a clone of the internal id collection.
        #[must_use]
        pub fn clone_inner(&self) -> Arc<Mutex<[<InnerBag $int_name>]>> {
          Arc::clone(&self.0)
        }


        #[doc = r#"
        Allocate a new identifier.

        ```
        use idbag::IdBag"# $int_name r#";

        let idbag = IdBag"# $int_name r#"::new();

        let id = idbag.alloc();
        assert_eq!(id.val(), 1);

        let id = idbag.alloc();
        assert_eq!(id.val(), 2);
        ```

        # Desgin flaws
        This function will hang if all the id's have been exhausted.
        "#]
        #[must_use]
        pub fn alloc(&self) -> [<Id $int_name>] {
          let mut g = self.0.lock();
          // ToDo: exhaust-deadlock
          let id = loop {
            g.idgen = g.idgen.wrapping_add(1);
            if !g.set.contains(&g.idgen) {
              break g.idgen;
            }
          };
          g.set.insert(id);
          drop(g);
          [<Id $int_name>] {
            id,
            idbag: Arc::downgrade(&self.0)
          }
        }


        #[doc = r#"
        Allocate a specific id, if available.

        ```
        use idbag::IdBag"# $int_name r#";

        let idbag = IdBag"# $int_name r#"::new();

        let Some(id) = idbag.alloc_id(42) else {
          panic!("Unexpectedly unable to allocate id 42");
        };
        assert_eq!(id.val(), 42);

        let id = idbag.alloc_id(42);
        assert_eq!(id, None);
        ```
        "#]
        #[must_use]
        pub fn alloc_id(&self, id: $int_type) -> Option<[<Id $int_name>]> {
          let mut g = self.0.lock();
          if g.set.contains(&id) {
            None
          } else {
            g.set.insert(id);
            drop(g);
            Some([<Id $int_name>] {
              id,
              idbag: Arc::downgrade(&self.0)
            })
          }
        }
      }
    }

    paste! {
      #[doc = r#"
      An allocated `"# $int_type r#"` identifier.
      "#]
      pub struct [<Id $int_name>] {
        id: $int_type,
        idbag: Weak<Mutex<[<InnerBag $int_name>]>>
      }

      impl fmt::Debug for [<Id $int_name>] {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
          write!(f, "Id:{}", self.id)
        }
      }

      impl Hash for [<Id $int_name>] {
        fn hash<H>(&self, state: &mut H)
        where
          H: Hasher
        {
          self.id.hash(state);
        }
      }

      impl PartialEq for [<Id $int_name>] {
        fn eq(&self, other: &Self) -> bool {
          self.id == other.id
        }
      }

      impl Eq for [<Id $int_name>] {}

      impl Borrow<$int_type> for [<Id $int_name>] {
        #[doc = r#"
        Allow `Id"# $int_name r#"` to be borrowed as a `&"# $int_type r#"`.

        This allows a `HashMap<Id"# $int_name r#", T>` to look up entries
        using a `&"# $int_type r#"`.
        "#]
        fn borrow(&self) -> &$int_type {
          &self.id
        }
      }

      impl [<Id $int_name>] {
        #[doc = r#"Return the internal `"# $int_type r#"` value."#]
        #[must_use]
        #[deprecated(since = "0.2.0", note = "Use `get()` method instead.")]
        pub const fn val(&self) -> $int_type {
          self.id
        }

        #[doc = r#"Return the internal `"# $int_type r#"` value."#]
        #[must_use]
        pub const fn get(&self) -> $int_type {
          self.id
        }

        #[doc = r#"
        Turn object into an [`ArcId"# $int_name r#"`].
        ```
        let idbag = idbag::IdBag"# $int_name r#"::new();

        let aid = idbag.alloc().into_arcid();
        ```
        "#]
        #[must_use]
        pub fn into_arcid(self) -> [<ArcId $int_name>] {
          [<ArcId $int_name>](Arc::new(self))
        }
      }

      impl Drop for [<Id $int_name>] {
        #[doc = r#"
        When an [`Id"# $int_name r#"`] is dropped, return the internal integer
        to the collection.
        "#]
        fn drop(&mut self) {
          if let Some(idbag) = self.idbag.upgrade() {
            let mut g = idbag.lock();
            g.set.remove(&self.id);
          }
        }
      }
    }


    paste! {
      #[doc = r#"
      An atomically referenced counted version of [`Id"# $int_name r#"`].

      This exists primarily because it should be possible to look up
      `Arc<Id"# $int_name r#">`'s by `"# $int_type r#"`'s in `HashMap`'s.
      While it is possible to look up `Id`'s in `HashMap`'s using
      `&"# $int_type r#"`'s, it doesn't work for `Arc<Id"# $int_name r#">`'s.
      To workaround this limitation this `ArcId"# $int_name r#"` newtype
      implements `Borrow<"# $int_type r#">`, which passes it through the
      `Arc` to it's inner `Id"# $int_name r#"`.
      "#]
      #[derive(Clone, Hash, PartialEq, Eq)]
      #[repr(transparent)]
      pub struct [<ArcId $int_name>](Arc<[<Id $int_name>]>);

      impl fmt::Debug for [<ArcId $int_name>] {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
          write!(f, "Id:{}", self.0.id)
        }
      }

      impl [<ArcId $int_name>] {
        #[doc = r#"
        Return the internal identifier `"# $int_type r#"` value.
        "#]
        #[must_use]
        pub fn val(&self) -> $int_type {
          self.0.get()
        }

        #[doc = r#"
        Returns the inner value, if the `ArcId` has exactly one strong
        reference.

        # Errors
        If there's more than one strong reference to the
        `ArcId"# $int_name r#"`, the `ArcId"# $int_name r#"` will be returned.
        "#]
        pub fn try_unwrap(this: Self) -> Result<[<Id $int_name>], Self> {
          Arc::<[<Id $int_name>]>::try_unwrap(this.0)
            .map_err([<ArcId $int_name>])
        }

        #[doc = r#"
        Returns the inner value, if the `ArcId"# $int_name r#"` has exactly
        one strong reference.
        "#]
        #[must_use]
        pub fn into_inner(this: Self) -> Option<[<Id $int_name>]> {
          Arc::<[<Id $int_name>]>::into_inner(this.0)
        }
      }

      impl Borrow<$int_type> for [<ArcId $int_name>] {
        fn borrow(&self) -> &$int_type {
          (*self.0).borrow()
        }
      }
    }
  };
}

impl_Bag! { u8 U8 }
impl_Bag! { u16 U16 }
impl_Bag! { u32 U32 }
impl_Bag! { u64 U64 }
impl_Bag! { usize Usize }

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :

Changes to tests/arc.rs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use idbag::{ArcId, IdBag};

#[test]
fn arc() {
  let idbag = IdBag::new();
  let id = idbag.alloc().into_arcid();
  let id2 = id.clone();

  assert_eq!(ArcId::into_inner(id2), None);
  let Some(id) = ArcId::into_inner(id) else {
    panic!("Unable to into_inner()");
  };
  assert_eq!(id.val(), 1);
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
|



|



|
|


|



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use idbag::{ArcIdU32, IdBagU32};

#[test]
fn arc() {
  let idbag = IdBagU32::new();
  let id = idbag.alloc().into_arcid();
  let id2 = id.clone();

  assert_eq!(ArcIdU32::into_inner(id2), None);
  let Some(id) = ArcIdU32::into_inner(id) else {
    panic!("Unable to into_inner()");
  };
  assert_eq!(id.get(), 1);
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :

Changes to tests/map_access.rs.

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
use hashbrown::HashMap;

use idbag::{ArcId, Id, IdBag};

#[test]
fn access() {
  let mut m: HashMap<Id, String> = HashMap::new();
  let idbag = IdBag::new();
  let id = idbag.alloc();
  let idval = id.val();

  m.insert(id, String::from("hello"));

  // Look up using &u32
  if let Some(v) = m.get(&idval) {
    assert_eq!(v, &String::from("hello"));
  } else {
    panic!("Unable to find entry");
  }
}

#[test]
fn arced() {
  let mut m: HashMap<ArcId, String> = HashMap::new();
  let idbag = IdBag::new();
  let id = idbag.alloc().into_arcid();
  let idval = id.val();



  m.insert(id.clone(), String::from("hello"));

  // Look up using &u32
  if let Some(v) = m.get(&idval) {
    assert_eq!(v, &String::from("hello"));
  } else {
    panic!("Unable to find entry");


|



|
|

|













|
|



>
>







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
use hashbrown::HashMap;

use idbag::{ArcIdU32, IdBagU32, IdU32};

#[test]
fn access() {
  let mut m: HashMap<IdU32, String> = HashMap::new();
  let idbag = IdBagU32::new();
  let id = idbag.alloc();
  let idval = id.get();

  m.insert(id, String::from("hello"));

  // Look up using &u32
  if let Some(v) = m.get(&idval) {
    assert_eq!(v, &String::from("hello"));
  } else {
    panic!("Unable to find entry");
  }
}

#[test]
fn arced() {
  let mut m: HashMap<ArcIdU32, String> = HashMap::new();
  let idbag = IdBagU32::new();
  let id = idbag.alloc().into_arcid();
  let idval = id.val();

  // Actually testing the clone here, so don't warn about it
  #[allow(clippy::redundant_clone)]
  m.insert(id.clone(), String::from("hello"));

  // Look up using &u32
  if let Some(v) = m.get(&idval) {
    assert_eq!(v, &String::from("hello"));
  } else {
    panic!("Unable to find entry");

Changes to www/changelog.md.

1
2


3
4
5
6
7
8
9



10
11
12
13
14
15
16
# Change Log



## [Unreleased]

[Details](/vdiff?from=idbag-0.1.2&to=trunk)

### Added

### Changed




### Removed

---

## [0.1.2] - 2024-01-23



>
>







>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Change Log

⚠️  indicates a breaking change.

## [Unreleased]

[Details](/vdiff?from=idbag-0.1.2&to=trunk)

### Added

### Changed

- ⚠️ Implement for explicit integer types.  Instead of `IdBag`, `Id`, etc, now
  uses `IdBag{U8,U16,U32,U64,Usize}`, `Id{U8,U16,..}`, etc.

### Removed

---

## [0.1.2] - 2024-01-23

Changes to www/index.md.

1
2
3
4
5
6
7
8
9
10
11
# idbag

A bag that initially contains (all) `u32` values that can be allocated and
returned to the bag.


## Change log

The details of changes can always be found in the timeline, but for a
high-level view of changes between released versions there's a manually
maintained [Change Log](./changelog.md).


<
|







1
2

3
4
5
6
7
8
9
10
# idbag


A bag of integers that can be allocated and then returned to the bag.


## Change log

The details of changes can always be found in the timeline, but for a
high-level view of changes between released versions there's a manually
maintained [Change Log](./changelog.md).